feat: 添加API接口,编写单元测试
This commit is contained in:
parent
377ca8cba3
commit
a6ee2ac518
34
pom.xml
34
pom.xml
|
|
@ -16,8 +16,8 @@
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<!-- 跳过测试 -->
|
<!-- 跳过测试 -->
|
||||||
<skipTests>true</skipTests>
|
<skipTests>false</skipTests>
|
||||||
<maven.test.skip>true</maven.test.skip>
|
<maven.test.skip>false</maven.test.skip>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
@ -25,7 +25,11 @@
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 移除了spring-boot-starter-test依赖 -->
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|
@ -38,7 +42,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.18.36</version>
|
<version>1.18.38</version>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
@ -109,10 +113,30 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- 配置maven-compiler-plugin跳过测试 -->
|
<!-- 配置maven-compiler-plugin -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<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>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
<resources>
|
<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;
|
private AdminInterceptor adminInterceptor;
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlayerInterceptor playerInterceptor;
|
private PlayerInterceptor playerInterceptor;
|
||||||
|
@Autowired
|
||||||
|
private ApiKeyInterceptor apiKeyInterceptor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
|
@ -23,8 +25,11 @@ public class WebConfig implements WebMvcConfigurer {
|
||||||
"/admin/login/**",
|
"/admin/login/**",
|
||||||
"/api/answer/**",
|
"/api/answer/**",
|
||||||
"/api/confirm",
|
"/api/confirm",
|
||||||
"/api/region/findRegion"
|
"/api/region/findRegion",
|
||||||
|
"/api/whitelist/**"
|
||||||
);
|
);
|
||||||
|
registry.addInterceptor(apiKeyInterceptor)
|
||||||
|
.addPathPatterns("/api/whitelist/**");
|
||||||
registry.addInterceptor(playerInterceptor)
|
registry.addInterceptor(playerInterceptor)
|
||||||
.addPathPatterns(
|
.addPathPatterns(
|
||||||
"/player/**",
|
"/player/**",
|
||||||
|
|
|
||||||
|
|
@ -41,5 +41,7 @@ minecraft:
|
||||||
heart-time: 600
|
heart-time: 600
|
||||||
test-cmd: ping
|
test-cmd: ping
|
||||||
add-cmd: login whitelist
|
add-cmd: login whitelist
|
||||||
|
api:
|
||||||
|
key: ${API_KEY:changeme-3944realms-whitelist-key}
|
||||||
email:
|
email:
|
||||||
enable: false
|
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>
|
<div class="timeline-badge primary"></div>
|
||||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||||
<span>最近</span>
|
<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"
|
class="img-fluid w-100"
|
||||||
src="/pic/1_1.png"></h6>
|
src="/pic/1_1.png"></h6>
|
||||||
<p class="mb-0">终于玩上机械动力了/(ㄒoㄒ)/~~</p>
|
<p class="mb-0">机械动力,太卡了XwX</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="timeline-badge info"></div>
|
<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