feat: 添加API接口,编写单元测试
This commit is contained in:
parent
377ca8cba3
commit
a6ee2ac518
34
pom.xml
34
pom.xml
|
|
@ -16,8 +16,8 @@
|
|||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<!-- 跳过测试 -->
|
||||
<skipTests>true</skipTests>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
<skipTests>false</skipTests>
|
||||
<maven.test.skip>false</maven.test.skip>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
@ -25,7 +25,11 @@
|
|||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 移除了spring-boot-starter-test依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
|
@ -38,7 +42,7 @@
|
|||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
<version>1.18.38</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
@ -109,10 +113,30 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- 配置maven-compiler-plugin跳过测试 -->
|
||||
<!-- 配置maven-compiler-plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.38</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- 配置maven-surefire-plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- 排除需要数据库的 @SpringBootTest -->
|
||||
<excludes>
|
||||
<exclude>**/MinecraftManagerApplicationTests.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
package com.linearpast.minecraftmanager.controller;
|
||||
|
||||
import com.linearpast.minecraftmanager.entity.Operators;
|
||||
import com.linearpast.minecraftmanager.entity.Players;
|
||||
import com.linearpast.minecraftmanager.entity.view.PlayerInfoView;
|
||||
import com.linearpast.minecraftmanager.service.inter.PlayersService;
|
||||
import com.linearpast.minecraftmanager.utils.Result;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/whitelist")
|
||||
public class WhitelistController {
|
||||
|
||||
@Autowired
|
||||
private PlayersService playersService;
|
||||
|
||||
/**
|
||||
* 获取白名单列表(已通过的玩家,status=1)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<?> getWhitelist(
|
||||
@RequestParam(required = false) String playerName,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size
|
||||
) {
|
||||
Page<PlayerInfoView> players = playersService.getPlayers(
|
||||
playerName, null, null, (byte) 1,
|
||||
null, null, PageRequest.of(page - 1, size)
|
||||
);
|
||||
return Result.successPage(players.getContent(), players.getTotalElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待审核列表(status=2)
|
||||
*/
|
||||
@GetMapping("/pending")
|
||||
public Result<?> getPending(
|
||||
@RequestParam(required = false) String playerName,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size
|
||||
) {
|
||||
Page<PlayerInfoView> players = playersService.getPlayers(
|
||||
playerName, null, null, (byte) 2,
|
||||
null, null, PageRequest.of(page - 1, size)
|
||||
);
|
||||
return Result.successPage(players.getContent(), players.getTotalElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被拒绝列表(status=0)
|
||||
*/
|
||||
@GetMapping("/rejected")
|
||||
public Result<?> getRejected(
|
||||
@RequestParam(required = false) String playerName,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size
|
||||
) {
|
||||
Page<PlayerInfoView> players = playersService.getPlayers(
|
||||
playerName, null, null, (byte) 0,
|
||||
null, null, PageRequest.of(page - 1, size)
|
||||
);
|
||||
return Result.successPage(players.getContent(), players.getTotalElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过审核(加入白名单)
|
||||
*/
|
||||
@PostMapping("/approve/{id}")
|
||||
public Result<?> approve(@PathVariable Integer id, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayerStatus(id, (byte) 1, operators);
|
||||
return code > 0 ? Result.success().msg("已加入白名单") : Result.error("操作失败,Rcon连接错误或玩家不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝申请
|
||||
*/
|
||||
@PostMapping("/reject/{id}")
|
||||
public Result<?> reject(@PathVariable Integer id, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayerStatus(id, (byte) 0, operators);
|
||||
return code > 0 ? Result.success().msg("已拒绝申请") : Result.error("操作失败,Rcon连接错误或玩家不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
* 从白名单移除
|
||||
*/
|
||||
@DeleteMapping("/remove/{id}")
|
||||
public Result<?> removeFromWhitelist(@PathVariable Integer id) {
|
||||
return playersService.deletePlayer(id) ? Result.success().msg("已从白名单移除") : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量通过
|
||||
*/
|
||||
@PostMapping("/batchApprove")
|
||||
public Result<?> batchApprove(@RequestBody List<Integer> ids, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayersStatus(ids, (byte) 1, operators);
|
||||
return code > 0 ? Result.success().msg("成功处理:" + code + "/" + ids.size()) : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量拒绝
|
||||
*/
|
||||
@PostMapping("/batchReject")
|
||||
public Result<?> batchReject(@RequestBody List<Integer> ids, HttpSession session) {
|
||||
Operators operators = (Operators) session.getAttribute("adminAccount");
|
||||
int code = playersService.updatePlayersStatus(ids, (byte) 0, operators);
|
||||
return code > 0 ? Result.success().msg("成功处理:" + code + "/" + ids.size()) : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量移除白名单
|
||||
*/
|
||||
@DeleteMapping("/batchRemove")
|
||||
public Result<?> batchRemove(@RequestBody List<Integer> ids) {
|
||||
int code = playersService.deletePlayers(ids);
|
||||
return code > 0 ? Result.success().msg("成功移除:" + code + "/" + ids.size()) : Result.error("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取白名单统计信息
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public Result<?> getStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("approved", playersService.getPlayersCountByStatus((byte) 1));
|
||||
stats.put("pending", playersService.getPlayersCountByStatus((byte) 2));
|
||||
stats.put("rejected", playersService.getPlayersCountByStatus((byte) 0));
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询玩家白名单状态
|
||||
*/
|
||||
@GetMapping("/check/{playerName}")
|
||||
public Result<?> checkPlayer(@PathVariable String playerName) {
|
||||
Players player = playersService.getPlayer(playerName);
|
||||
if (player == null) {
|
||||
return Result.success(Map.of("exists", false));
|
||||
}
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("exists", true);
|
||||
info.put("status", player.getStatus());
|
||||
info.put("playerName", player.getPlayerName());
|
||||
info.put("qq", player.getQq());
|
||||
info.put("uuid", player.getUuid());
|
||||
String statusText = switch (player.getStatus()) {
|
||||
case 1 -> "已通过";
|
||||
case 2 -> "待审核";
|
||||
default -> "已拒绝";
|
||||
};
|
||||
info.put("statusText", statusText);
|
||||
return Result.success(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家得分
|
||||
*/
|
||||
@GetMapping("/score/{id}")
|
||||
public Result<?> getPlayerScore(@PathVariable Integer id) {
|
||||
Integer score = playersService.getPlayerScoreById(id);
|
||||
if (score == null) {
|
||||
return Result.error("玩家不存在");
|
||||
}
|
||||
return Result.success(Map.of("playerId", id, "totalScore", score));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.linearpast.minecraftmanager.interceptor;
|
||||
|
||||
import com.linearpast.minecraftmanager.exception.UnauthorizedException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
@Component
|
||||
public class ApiKeyInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Value("${api.key}")
|
||||
private String apiKey;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 1. 优先检查 API Key(Header: X-API-Key 或 Query: apiKey)
|
||||
String reqApiKey = request.getHeader("X-API-Key");
|
||||
if (!StringUtils.hasText(reqApiKey)) {
|
||||
reqApiKey = request.getParameter("apiKey");
|
||||
}
|
||||
if (StringUtils.hasText(reqApiKey) && apiKey.equals(reqApiKey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 回退到 Session 鉴权(浏览器访问)
|
||||
HttpSession session = request.getSession();
|
||||
if (session != null
|
||||
&& session.getAttribute("isLoggedIn") != null
|
||||
&& (boolean) session.getAttribute("isLoggedIn")
|
||||
&& session.getAttribute("adminAccount") != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new UnauthorizedException("redirect:/admin/login?error=please login first");
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
private AdminInterceptor adminInterceptor;
|
||||
@Autowired
|
||||
private PlayerInterceptor playerInterceptor;
|
||||
@Autowired
|
||||
private ApiKeyInterceptor apiKeyInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
|
|
@ -23,8 +25,11 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
"/admin/login/**",
|
||||
"/api/answer/**",
|
||||
"/api/confirm",
|
||||
"/api/region/findRegion"
|
||||
"/api/region/findRegion",
|
||||
"/api/whitelist/**"
|
||||
);
|
||||
registry.addInterceptor(apiKeyInterceptor)
|
||||
.addPathPatterns("/api/whitelist/**");
|
||||
registry.addInterceptor(playerInterceptor)
|
||||
.addPathPatterns(
|
||||
"/player/**",
|
||||
|
|
|
|||
|
|
@ -41,5 +41,7 @@ minecraft:
|
|||
heart-time: 600
|
||||
test-cmd: ping
|
||||
add-cmd: login whitelist
|
||||
api:
|
||||
key: ${API_KEY:changeme-3944realms-whitelist-key}
|
||||
email:
|
||||
enable: false
|
||||
|
|
|
|||
BIN
src/main/resources/static/pic/1-1.png
Normal file
BIN
src/main/resources/static/pic/1-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 MiB |
|
|
@ -107,11 +107,16 @@
|
|||
<div class="timeline-badge primary"></div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>最近</span>
|
||||
<h6 class="mb-0">服务器迈入机械动力时代,而且是1.20.1……<img alt=""
|
||||
<h6 class="mb-0">8周目合拍照<img alt=""
|
||||
class="img-fluid w-100"
|
||||
src="/pic/1-1.png"></h6>
|
||||
<p class="mb-0">来也匆匆,去也匆匆</p>
|
||||
<h6 class="mb-0">8周目刚开始时<img alt=""
|
||||
class="img-fluid w-100"
|
||||
src="/pic/1_1.png"></h6>
|
||||
<p class="mb-0">终于玩上机械动力了/(ㄒoㄒ)/~~</p>
|
||||
<p class="mb-0">机械动力,太卡了XwX</p>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge info"></div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,366 @@
|
|||
package com.linearpast.minecraftmanager.controller;
|
||||
|
||||
import com.linearpast.minecraftmanager.entity.Operators;
|
||||
import com.linearpast.minecraftmanager.entity.Players;
|
||||
import com.linearpast.minecraftmanager.entity.view.PlayerInfoView;
|
||||
import com.linearpast.minecraftmanager.service.inter.PlayersService;
|
||||
import com.linearpast.minecraftmanager.utils.Result;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WhitelistControllerTest {
|
||||
|
||||
@Mock
|
||||
private PlayersService playersService;
|
||||
|
||||
@InjectMocks
|
||||
private WhitelistController controller;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
private MockHttpSession adminSession;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
|
||||
Operators admin = new Operators();
|
||||
admin.setId(1);
|
||||
admin.setUsername("testAdmin");
|
||||
|
||||
adminSession = new MockHttpSession();
|
||||
adminSession.setAttribute("adminAccount", admin);
|
||||
adminSession.setAttribute("isLoggedIn", true);
|
||||
}
|
||||
|
||||
// ==================== list ====================
|
||||
|
||||
@Test
|
||||
void getWhitelist_shouldReturnPage() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 1),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/list")
|
||||
.param("page", "1").param("size", "10"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.count").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhitelist_withPlayerName_shouldFilter() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(eq("testPlayer"), isNull(), isNull(), eq((byte) 1),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/list").param("playerName", "testPlayer"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== pending ====================
|
||||
|
||||
@Test
|
||||
void getPending_shouldReturnPage() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 2),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/pending"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== rejected ====================
|
||||
|
||||
@Test
|
||||
void getRejected_shouldReturnPage() throws Exception {
|
||||
Page<PlayerInfoView> page = new PageImpl<>(Collections.emptyList());
|
||||
when(playersService.getPlayers(isNull(), isNull(), isNull(), eq((byte) 0),
|
||||
isNull(), isNull(), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/rejected"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== approve ====================
|
||||
|
||||
@Test
|
||||
void approve_success() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), any(Operators.class))).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/1").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("已加入白名单"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void approve_failure() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(99), eq((byte) 1), any(Operators.class))).thenReturn(0);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/99").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
@Test
|
||||
void approve_withoutSession_passNullOperator() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), isNull())).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== reject ====================
|
||||
|
||||
@Test
|
||||
void reject_success() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 0), any(Operators.class))).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/reject/1").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("已拒绝申请"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reject_failure() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(99), eq((byte) 0), any(Operators.class))).thenReturn(0);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/reject/99").session(adminSession))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== remove ====================
|
||||
|
||||
@Test
|
||||
void removeFromWhitelist_success() throws Exception {
|
||||
when(playersService.deletePlayer(1)).thenReturn(true);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/remove/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("已从白名单移除"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeFromWhitelist_failure() throws Exception {
|
||||
when(playersService.deletePlayer(99)).thenReturn(false);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/remove/99"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== batchApprove ====================
|
||||
|
||||
@Test
|
||||
void batchApprove_success() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2, 3);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(3);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchApprove")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2,3]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("成功处理:3/3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchApprove_partialSuccess() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2, 3);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(2);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchApprove")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2,3]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.msg").value("成功处理:2/3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchApprove_allFailed() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 1), any(Operators.class))).thenReturn(0);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchApprove")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== batchReject ====================
|
||||
|
||||
@Test
|
||||
void batchReject_success() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2);
|
||||
when(playersService.updatePlayersStatus(eq(ids), eq((byte) 0), any(Operators.class))).thenReturn(2);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/batchReject")
|
||||
.session(adminSession)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
// ==================== batchRemove ====================
|
||||
|
||||
@Test
|
||||
void batchRemove_success() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2, 3);
|
||||
when(playersService.deletePlayers(ids)).thenReturn(3);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/batchRemove")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2,3]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.msg").value("成功移除:3/3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchRemove_allFailed() throws Exception {
|
||||
List<Integer> ids = List.of(1, 2);
|
||||
when(playersService.deletePlayers(ids)).thenReturn(0);
|
||||
|
||||
mockMvc.perform(delete("/api/whitelist/batchRemove")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("[1,2]"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500));
|
||||
}
|
||||
|
||||
// ==================== stats ====================
|
||||
|
||||
@Test
|
||||
void getStats_shouldReturnCounts() throws Exception {
|
||||
when(playersService.getPlayersCountByStatus((byte) 1)).thenReturn(10);
|
||||
when(playersService.getPlayersCountByStatus((byte) 2)).thenReturn(5);
|
||||
when(playersService.getPlayersCountByStatus((byte) 0)).thenReturn(3);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/stats"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.approved").value(10))
|
||||
.andExpect(jsonPath("$.data.pending").value(5))
|
||||
.andExpect(jsonPath("$.data.rejected").value(3));
|
||||
}
|
||||
|
||||
// ==================== check ====================
|
||||
|
||||
@Test
|
||||
void checkPlayer_exists() throws Exception {
|
||||
Players player = new Players();
|
||||
player.setPlayerName("Steve");
|
||||
player.setQq("12345");
|
||||
player.setUuid("uuid-123");
|
||||
player.setStatus((byte) 1);
|
||||
|
||||
when(playersService.getPlayer("Steve")).thenReturn(player);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/Steve"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.exists").value(true))
|
||||
.andExpect(jsonPath("$.data.status").value(1))
|
||||
.andExpect(jsonPath("$.data.statusText").value("已通过"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkPlayer_notExists() throws Exception {
|
||||
when(playersService.getPlayer("Notch")).thenReturn(null);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/Notch"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.exists").value(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkPlayer_pendingStatus() throws Exception {
|
||||
Players player = new Players();
|
||||
player.setStatus((byte) 2);
|
||||
|
||||
when(playersService.getPlayer("Newbie")).thenReturn(player);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/Newbie"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.statusText").value("待审核"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkPlayer_rejectedStatus() throws Exception {
|
||||
Players player = new Players();
|
||||
player.setStatus((byte) 0);
|
||||
|
||||
when(playersService.getPlayer("RejectedGuy")).thenReturn(player);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/check/RejectedGuy"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.statusText").value("已拒绝"));
|
||||
}
|
||||
|
||||
// ==================== score ====================
|
||||
|
||||
@Test
|
||||
void getPlayerScore_success() throws Exception {
|
||||
when(playersService.getPlayerScoreById(1)).thenReturn(85);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/score/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.playerId").value(1))
|
||||
.andExpect(jsonPath("$.data.totalScore").value(85));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlayerScore_notFound() throws Exception {
|
||||
when(playersService.getPlayerScoreById(999)).thenReturn(null);
|
||||
|
||||
mockMvc.perform(get("/api/whitelist/score/999"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500))
|
||||
.andExpect(jsonPath("$.msg").value("玩家不存在"));
|
||||
}
|
||||
|
||||
// ==================== session: null operator for API key calls ====================
|
||||
|
||||
@Test
|
||||
void approve_apiKeyCall_shouldPassNullOperator() throws Exception {
|
||||
when(playersService.updatePlayerStatus(eq(1), eq((byte) 1), isNull())).thenReturn(1);
|
||||
|
||||
mockMvc.perform(post("/api/whitelist/approve/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package com.linearpast.minecraftmanager.interceptor;
|
||||
|
||||
import com.linearpast.minecraftmanager.entity.Operators;
|
||||
import com.linearpast.minecraftmanager.exception.UnauthorizedException;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class ApiKeyInterceptorTest {
|
||||
|
||||
private static final String VALID_API_KEY = "test-api-key-123";
|
||||
|
||||
private ApiKeyInterceptor interceptor;
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
interceptor = new ApiKeyInterceptor();
|
||||
ReflectionTestUtils.setField(interceptor, "apiKey", VALID_API_KEY);
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
}
|
||||
|
||||
// ==================== API Key Header ====================
|
||||
|
||||
@Test
|
||||
void validApiKeyInHeader_shouldPass() {
|
||||
request.addHeader("X-API-Key", VALID_API_KEY);
|
||||
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidApiKeyInHeader_shouldThrow() {
|
||||
request.addHeader("X-API-Key", "wrong-key");
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyApiKeyInHeader_shouldFallbackToSession() {
|
||||
request.addHeader("X-API-Key", "");
|
||||
|
||||
// no session, should throw
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
// ==================== API Key Query Param ====================
|
||||
|
||||
@Test
|
||||
void validApiKeyInQueryParam_shouldPass() {
|
||||
request.setParameter("apiKey", VALID_API_KEY);
|
||||
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidApiKeyInQueryParam_shouldThrow() {
|
||||
request.setParameter("apiKey", "wrong-key");
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void headerTakesPriorityOverQueryParam() {
|
||||
request.addHeader("X-API-Key", VALID_API_KEY);
|
||||
request.setParameter("apiKey", "wrong-key");
|
||||
|
||||
// header should be used and pass
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
// ==================== Session Fallback ====================
|
||||
|
||||
@Test
|
||||
void validAdminSession_shouldPass() {
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("isLoggedIn", true);
|
||||
session.setAttribute("adminAccount", new Operators());
|
||||
|
||||
boolean result = interceptor.preHandle(request, response, null);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionNotLoggedIn_shouldThrow() {
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("isLoggedIn", false);
|
||||
session.setAttribute("adminAccount", new Operators());
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionNoAdminAccount_shouldThrow() {
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("isLoggedIn", true);
|
||||
// no adminAccount set
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noSession_noApiKey_shouldThrow() {
|
||||
// no session created, no API key set
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSession_noApiKey_shouldThrow() {
|
||||
// explicitly test null session behavior
|
||||
// MockHttpServletRequest.getSession() returns non-null by default,
|
||||
// but we test invalid session attributes above which covers this path
|
||||
|
||||
assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
|
||||
.isInstanceOf(UnauthorizedException.class);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user