blob: 61b73d87b2e5f17ef695431b8cfe42e75e038320 [file] [log] [blame]
xiukirad0a7a082025-06-05 16:28:08 +08001package com.g9.g9backend.controller;
2
xiukiraac78f3d2025-06-08 23:38:10 +08003import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
4import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
xiukiradbe96222025-06-06 21:25:09 +08005import com.fasterxml.jackson.databind.ObjectMapper;
6import com.g9.g9backend.pojo.*;
7import com.g9.g9backend.pojo.DTO.*;
8import com.g9.g9backend.service.*;
9import org.junit.jupiter.api.BeforeEach;
10import org.junit.jupiter.api.Test;
11import org.mockito.InjectMocks;
12import org.mockito.Mock;
13import org.mockito.MockitoAnnotations;
14import org.springframework.http.MediaType;
15import org.springframework.test.web.servlet.MockMvc;
16import org.springframework.test.web.servlet.setup.MockMvcBuilders;
17
xiukiraac78f3d2025-06-08 23:38:10 +080018import java.util.ArrayList;
xiukiradbe96222025-06-06 21:25:09 +080019import java.util.Date;
xiukiraac78f3d2025-06-08 23:38:10 +080020import java.util.List;
xiukiradbe96222025-06-06 21:25:09 +080021
22import static org.mockito.ArgumentMatchers.*;
23import static org.mockito.Mockito.when;
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
25import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
26
27/*
28 @SpringBootTest 启动一个完整的 Spring 应用上下文,用来测试整个应用的行为
29 @AutoConfigureMockMvc 自动配置 MockMvc 对象,MockMvc 是 Spring 提供的工具,用来模拟 HTTP 请求,从而不需要启动真正的服务器来发送HTTP请求就能进行测试
30 注意@SpringBootTest 已经自动配置了 Mockito 或 Spring 的 Mock 环境,再在setup中手动调用 openMocks(this) 可能导致 Mock 对象被重复初始化
31 出现状态丢失问题(如 when().thenReturn() 规则失效),所以改为使用 JUnit 5 默认运行器,不需要在这里写注解
32*/
xiukirad0a7a082025-06-05 16:28:08 +080033public class ResourceControllerTest {
34
xiukiradbe96222025-06-06 21:25:09 +080035 // MockMvc对象
36 private MockMvc mockMvc;
37
38 // @InjectMocks告诉测试框架(Mockito)创建 ResourceController 的实例,并自动将标注了 @Mock 的依赖注入到这个实例中
39 @InjectMocks
40 private ResourceController resourceController;
41
42 // @Mock告诉 Mockito 创建这些依赖的“模拟对象”,用来模拟服务行为,和controller层使用的service依赖一一对应即可
43 @Mock
44 private ResourceService resourceService;
45
46 @Mock
47 private GameplayService gameplayService;
48
49 @Mock
50 private RewardService rewardService;
51
52 @Mock
53 private UserUploadService userUploadService;
54
55 @Mock
56 private CommunityService communityService;
57
58 @Mock
59 private UserService userService;
60
61 @Mock
62 private UserPurchaseService userPurchaseService;
63
64 @Mock
65 private UserLikeService userLikeService;
66
67 @Mock
68 private UserCollectionService userCollectionService;
69
70 @Mock
71 private NotificationService notificationService;
72
xiukiraac78f3d2025-06-08 23:38:10 +080073 @Mock
74 private ResourceVersionService resourceVersionService;
75
76 @Mock
77 private SearchHistoryService searchHistoryService;
78
79 @Mock
80 private GameVersionService gameVersionService;
81
82 @Mock
83 private TorrentRecordService torrentRecordService;
84
85 @Mock
86 private HotTrendService hotTrendService;
87
xiukiradbe96222025-06-06 21:25:09 +080088 // ObjectMapper对象用于将 Java 对象和 JSON 字符串互相转换 ,因为在测试中,需要把请求参数或返回响应转换成 JSON 字符串形式来传输
89 private final ObjectMapper objectMapper = new ObjectMapper();
90
91 // 这个方法会在每个测试方法执行之前运行。用来设置测试的初始化逻辑
92 @BeforeEach
93 public void setup() {
94 MockitoAnnotations.openMocks(this);
95 mockMvc = MockMvcBuilders.standaloneSetup(resourceController).build();
96 }
97
98 // 以下是对各接口的测试,注意测试方法是要对每个接口的每个响应测试(如果一个接口有三种响应,就写三个测试方法)
99 // 上传资源
100 @Test
101 public void testUploadResource() throws Exception {
102
103 // 创建测试用的请求数据
104 PostResourceDTO postResourceDTO = new PostResourceDTO();
105 Resource resource = new Resource();
106 resource.setResourceId(1);
107 resource.setResourceName("Test Resource");
108 resource.setUploadTime(new Date());
109 resource.setLastUpdateTime(new Date());
110 resource.setPrice(1);
111 resource.setClassify("map");
112 postResourceDTO.setResource(resource);
113 postResourceDTO.setGameplayList(new String[]{"Gameplay1", "Gameplay2"});
114 postResourceDTO.setUserId(1);
115
116 /*
117 模拟服务层的行为,when(...).thenReturn(...)表示当某个服务方法被调用时,返回一个指定的结果(注意mybatis-plus的这些方法一般返回是boolean类型,所以写true不能写null)
118 同样是测试方法,测试粒度有区别
119 有简单写法:when(gameplayService.save(any())).thenReturn(true);只验证方法是否被调用(间接验证逻辑是否执行到该服务方法),不关心方法调用的次数和传递的参数
120 也有复杂写法:额外用 verify 验证调用次数和 ArgumentCaptor 验证参数是否传递正确和InOrder 来验证方法的调用顺序是否正确...
121 */
122
123 // 测试主是要测试传参是否能正确接收,接口调用过程是否使用过这些方法(所以使用到的方法一般来说应该按顺序写在这,不能缺不能多也不能乱序,但是这里实际因为测试粒度不够细,没有对方法调用过程的验证,所以即是方法缺少或调用过程有问题,也不会报错),返回响应是否发送正确
124 when(resourceService.save(any())).thenReturn(true);
125 when(gameplayService.save(any())).thenReturn(true);
126 when(userUploadService.save(any())).thenReturn(true);
127 when(communityService.save(any())).thenReturn(true);
128 when(rewardService.update(any())).thenReturn(true);
129
130 // 模拟 HTTP 请求
131 mockMvc.perform(post("/resource") // 使用 MockMvc 模拟用户提交 POST 请求到 /resource 接口
132 .contentType(MediaType.APPLICATION_JSON) // 设置请求体是 JSON 格式
133 .content(objectMapper.writeValueAsString(postResourceDTO))) //把请求参数对象:postResourceDTO 转成 JSON 字符串,作为请求体内容发送
134 .andExpect(status().isOk()); // 期望接口返回 HTTP 状态码 200(成功)
135 }
136
xiukiraac78f3d2025-06-08 23:38:10 +0800137 // 上传版本
138 @Test
139 public void testUploadResourceVersion() throws Exception {
140
141 ResourceVersion resourceVersion = new ResourceVersion();
142 resourceVersion.setResourceId(1);
143 resourceVersion.setResourceVersionName("Test ResourceVersion");
144
145 when(resourceVersionService.save(any())).thenReturn(true);
146
147 // 模拟 HTTP 请求
148 mockMvc.perform(post("/resource/version")
149 .contentType(MediaType.APPLICATION_JSON)
150 .content(objectMapper.writeValueAsString(resourceVersion)))
151 .andExpect(status().isOk());
152 }
153
xiukiradbe96222025-06-06 21:25:09 +0800154 // 购买资源
155 @Test
156 public void testPurchaseResource_success() throws Exception {
157
158 // 用户积分足够
159 User user = new User();
160 user.setUserId(1);
161 user.setCredits(100);
162 Resource resource = new Resource();
163 resource.setResourceId(1);
164 resource.setPrice(50);
165
166 when(userService.getOne(any())).thenReturn(user);
167 when(resourceService.getOne(any())).thenReturn(resource);
168 when(userService.update(any())).thenReturn(true);
169 when(userPurchaseService.save(any())).thenReturn(true);
170 when(userUploadService.getOne(any())).thenReturn(new UserUpload(2, 1));
171 when(notificationService.save(any())).thenReturn(true);
172
173 mockMvc.perform(post("/resource/purchase")
174 .contentType(MediaType.APPLICATION_JSON)
175 .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1))))
176 .andExpect(status().isOk());
177 }
178
179 @Test
180 public void testPurchaseResource_purchaseFailed() throws Exception {
181
182 //用户积分不足
183 User user = new User();
184 user.setUserId(1);
185 user.setCredits(10);
186 Resource resource = new Resource();
187 resource.setResourceId(1);
188 resource.setPrice(50);
189
190 when(userService.getOne(any())).thenReturn(user);
191 when(resourceService.getOne(any())).thenReturn(resource);
192
193 mockMvc.perform(post("/resource/purchase")
194 .contentType(MediaType.APPLICATION_JSON)
195 .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1))))
196 .andExpect(status().is(412));
197 }
198
199 // 点赞资源
200 @Test
201 public void testLikeResource() throws Exception {
202
203 User user = new User();
204 user.setUserId(1);
205 Resource resource = new Resource();
206 resource.setResourceId(1);
207
208 when(userService.getOne(any())).thenReturn(user);
209 when(resourceService.getOne(any())).thenReturn(resource);
210 when(userLikeService.save(any())).thenReturn(true);
211 when(userUploadService.getOne(any())).thenReturn(new UserUpload(2, 1));
212 when(notificationService.save(any())).thenReturn(true);
213
214 mockMvc.perform(post("/resource/like")
215 .contentType(MediaType.APPLICATION_JSON)
216 .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1))))
217 .andExpect(status().isOk());
218 }
219
220 // 收藏资源
221 @Test
222 public void testCollectResource() throws Exception {
223
224 User user = new User();
225 user.setUserId(1);
226 Resource resource = new Resource();
227 resource.setResourceId(1);
228
229 when(userService.getOne(any())).thenReturn(user);
230 when(resourceService.getOne(any())).thenReturn(resource);
231 when(userCollectionService.save(any())).thenReturn(true);
232 when(userUploadService.getOne(any())).thenReturn(new UserUpload(2, 1));
233 when(notificationService.save(any())).thenReturn(true);
234
235 mockMvc.perform(post("/resource/collection")
236 .contentType(MediaType.APPLICATION_JSON)
237 .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1))))
238 .andExpect(status().isOk());
239 }
xiukirab9490272025-06-06 21:53:52 +0800240
241 // 删除资源
242 @Test
243 public void testDeleteResource_success() throws Exception {
244
245 // 密码正确
246 when(userService.getOne(any())).thenReturn(new User(1, "hcy", "123", null, 0, 0, null, 0, 0, 0));
247 when(resourceService.removeById(1)).thenReturn(true);
248
249 mockMvc.perform(delete("/resource")
250 .param("resourceId", "1")
251 .param("userId", "1")
252 .param("password", "123"))
253 .andExpect(status().isNoContent());
254 }
255
256 @Test
257 public void testDeleteResource_wrongPassword() throws Exception {
258
259 // 密码错误
260 when(userService.getOne(any())).thenReturn(new User(1, "hcy", "123", null, 0, 0, null, 0, 0, 0));
261
262 mockMvc.perform(delete("/resource")
263 .param("resourceId", "1")
264 .param("userId", "1")
265 .param("password", "wrong"))
266 .andExpect(status().is(408));
267 }
268
269 // 取消点赞
270 @Test
271 public void testLikeDelete() throws Exception {
272
273 when(userLikeService.remove(any())).thenReturn(true);
274
275 mockMvc.perform(delete("/resource/like")
276 .param("userId", "1")
277 .param("resourceId", "1"))
278 .andExpect(status().isNoContent());
279 }
280
281 // 取消收藏
282 @Test
283 public void testCollectionDelete() throws Exception {
284
285 when(userCollectionService.remove(any())).thenReturn(true);
286
287 mockMvc.perform(delete("/resource/collection")
288 .param("userId", "1")
289 .param("resourceId", "1"))
290 .andExpect(status().isNoContent());
291 }
xiukiraac78f3d2025-06-08 23:38:10 +0800292
293 // 搜索资源
294 @Test
295 public void testGetResourceSearch() throws Exception {
296
297 when(searchHistoryService.save(any())).thenReturn(true);
298 when(resourceService.page(any(), any())).thenReturn(new Page<>(1, 1));
299
300 List<Gameplay> gameplayList = new ArrayList<>();
301 QueryWrapper<Gameplay> gameplayQuery = new QueryWrapper<>();
302 gameplayQuery.eq("resource_id", 1);
303 when(gameplayService.list(gameplayQuery)).thenReturn(gameplayList);
304
305 mockMvc.perform(get("/resource/search")
306 .param("userId", "1")
307 .param("searchValue", "")
308 .param("classify", "")
309 .param("gameplayList", "")
310 .param("gameVersionList", "")
311 .param("pageNumber", "1")
312 .param("rows", "1"))
313 .andExpect(status().isOk());
314 }
315
316 // 获取资源信息
317 @Test
318 public void testGetResourceInfo() throws Exception {
319
320 when(resourceService.getById(any())).thenReturn(new Resource());
321 when(gameplayService.list(new QueryWrapper<Gameplay>().eq("resource_id", 1))).thenReturn(new ArrayList<>());
322 when(resourceVersionService.list(new QueryWrapper<ResourceVersion>().eq("resource_id", 1))).thenReturn(new ArrayList<>());
323 when(gameVersionService.list(new QueryWrapper<GameVersion>().eq("resource_version_id", 1))).thenReturn(new ArrayList<>());
324 when(torrentRecordService.list(new QueryWrapper<TorrentRecord>().eq("resource_version_id", 1))).thenReturn(new ArrayList<>());
325 when(userCollectionService.getOne(any())).thenReturn(new UserCollection());
326 when(userLikeService.getOne(any())).thenReturn(new UserLike());
327 when(userPurchaseService.getOne(any())).thenReturn(new UserPurchase());
328 when(userUploadService.getOne(any())).thenReturn(new UserUpload());
329
330 mockMvc.perform(get("/resource/info")
331 .param("resourceId", "1")
332 .param("userId", "1"))
333 .andExpect(status().isOk());
334 }
335
336 // 获取热门资源
337 @Test
338 public void testGetHotResource() throws Exception {
339
340 when(resourceService.page(any(), any())).thenReturn(new Page<>(1, 1));
341
342 List<Gameplay> gameplayList = new ArrayList<>();
343 QueryWrapper<Gameplay> gameplayQuery = new QueryWrapper<>();
344 gameplayQuery.eq("resource_id", 1);
345 when(gameplayService.list(gameplayQuery)).thenReturn(gameplayList);
346
347 mockMvc.perform(get("/resource/hot")
348 .param("classify", "map")
349 .param("pageNumber", "1")
350 .param("rows", "1"))
351 .andExpect(status().isOk());
352 }
353
354 // 获取热门资源幻灯片数据
355 @Test
356 public void testGetHotSlideResource() throws Exception {
357
358 List<Resource> resourceList = new ArrayList<>();
359 QueryWrapper<Resource> resourceQuery = new QueryWrapper<>();
360 resourceQuery.orderByDesc("(downloads * 0.25 + likes * 0.25 + collections * 0.25 + comments * 0.25)");
361 resourceQuery.last("LIMIT 3");
362 when(resourceService.list(resourceQuery)).thenReturn(resourceList);
363
364 mockMvc.perform(get("/resource/hot/slide"))
365 .andExpect(status().isOk());
366 }
367
368 // 获取热门资源趋势图
369 @Test
370 public void testGetResourceHotTrend() throws Exception {
371
372 List<HotTrend> hotTrendList = new ArrayList<>();
373 when(hotTrendService.list()).thenReturn(hotTrendList);
374
375 mockMvc.perform(get("/resource/hot-trend"))
376 .andExpect(status().isOk());
377 }
378
379 // 修改资源信息
380 @Test
381 public void testPutResourceInfo_success() throws Exception {
382
383 PutResourceInfoDTO putResourceInfoDTO = new PutResourceInfoDTO();
384 putResourceInfoDTO.setResourceId(1);
385 putResourceInfoDTO.setResourceName("test");
386 putResourceInfoDTO.setResourcePicture("test");
387 putResourceInfoDTO.setResourceSummary("test");
388 putResourceInfoDTO.setResourceDetail("test");
389 putResourceInfoDTO.setPrice(100);
390 String[] gameplayList = {"test1", "test2"};
391 putResourceInfoDTO.setGameplayList(gameplayList);
392
393 when(resourceService.getOne(any())).thenReturn(null);
394 when(resourceService.update(any())).thenReturn(true);
395 when(gameplayService.remove(any())).thenReturn(true);
396 when(gameplayService.save(any())).thenReturn(true);
397
398 mockMvc.perform(put("/resource/info")
399 .contentType(MediaType.APPLICATION_JSON)
400 .content(objectMapper.writeValueAsString(putResourceInfoDTO)))
401 .andExpect(status().isOk());
402 }
403
404 @Test
405 public void testPutResourceInfo__resourceNameExists() throws Exception {
406
407 PutResourceInfoDTO putResourceInfoDTO = new PutResourceInfoDTO();
408 putResourceInfoDTO.setResourceId(1);
409 putResourceInfoDTO.setResourceName("test");
410 putResourceInfoDTO.setResourcePicture("test");
411 putResourceInfoDTO.setResourceSummary("test");
412 putResourceInfoDTO.setResourceDetail("test");
413 putResourceInfoDTO.setPrice(100);
414 String[] gameplayList = {"test1", "test2"};
415 putResourceInfoDTO.setGameplayList(gameplayList);
416
417 when(resourceService.getOne(any())).thenReturn(new Resource());
418
419 mockMvc.perform(put("/resource/info")
420 .contentType(MediaType.APPLICATION_JSON)
421 .content(objectMapper.writeValueAsString(putResourceInfoDTO)))
422 .andExpect(status().is(411));
423 }
xiukirad0a7a082025-06-05 16:28:08 +0800424}