blob: 23890badfa722e22648369170199ddab8ddec3f5 [file] [log] [blame]
夜雨声烦368e3562025-04-24 01:49:46 +08001package com.example.g8backend.service;
2
3import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
4import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
5import com.example.g8backend.entity.Post;
6import com.example.g8backend.entity.PostView;
7import com.example.g8backend.mapper.CommentMapper;
8import com.example.g8backend.mapper.PostMapper;
9import com.example.g8backend.mapper.PostViewMapper;
10import com.example.g8backend.service.impl.PostServiceImpl;
11import org.junit.jupiter.api.BeforeEach;
12import org.junit.jupiter.api.Test;
13import org.junit.jupiter.api.extension.ExtendWith;
14import org.mockito.InjectMocks;
15import org.mockito.Mock;
16import org.mockito.junit.jupiter.MockitoExtension;
17import org.springframework.transaction.annotation.Transactional;
18
19import java.sql.Timestamp;
20import java.time.Instant;
21import java.util.Arrays;
22import java.util.Collections;
23import java.util.List;
24
25import static org.junit.jupiter.api.Assertions.*;
26import static org.mockito.ArgumentMatchers.*;
27import static org.mockito.Mockito.*;
28
29@ExtendWith(MockitoExtension.class)
30@Transactional
31public class PostServiceRecommendTest {
32
33 @Mock
34 private PostMapper postMapper;
35
36 @Mock
37 private PostViewMapper postViewMapper;
38
39 @Mock
40 private CommentMapper commentMapper;
41
42 @InjectMocks
43 private PostServiceImpl postService;
44
45 private Post mockPost;
46 private PostView mockPostView;
47
48 @BeforeEach
49 void setUp() {
50 // 初始化测试数据
51 mockPost = new Post()
52 .setPostId(1L)
53 .setUserId(100L)
54 .setPostTitle("Test Post")
55 .setViewCount(50)
56 .setCreatedAt(Timestamp.from(Instant.now().minusSeconds(7200)))
57 .setHotScore(5.0);
58
59 mockPostView = new PostView()
60 .setViewId(1L)
61 .setUserId(200L)
62 .setPostId(1L)
63 .setViewTime(Timestamp.from(Instant.now()).toLocalDateTime());
64 }
65
66 @Test
67 public void testGetRecommendedPosts_ExcludesViewedPosts() {
68 // 模拟用户已浏览的帖子ID
69 Long userId = 200L;
70 when(postViewMapper.findViewedPostIds(userId))
71 .thenReturn(Arrays.asList(1L, 2L));
72
73 // 模拟推荐结果(未浏览的帖子)
74 Post recommendedPost = new Post().setPostId(3L).setHotScore(8.0);
75 when(postMapper.selectPage(any(Page.class), any(QueryWrapper.class)))
76 .thenReturn(new Page<Post>().setRecords(Collections.singletonList(recommendedPost)));
77
78 // 调用推荐接口
79 Page<Post> result = postService.getRecommendedPosts(1, 10, userId);
80
81 // 验证结果
82 assertEquals(1, result.getRecords().size(), "应返回1条推荐结果");
83 assertEquals(3L, result.getRecords().get(0).getPostId(), "推荐结果应为未浏览的帖子ID 3");
84 assertFalse(result.getRecords().stream().anyMatch(p -> p.getPostId() == 1L), "结果中不应包含已浏览的帖子ID 1");
85 }
86
87 @Test
88 public void testGetRecommendedPosts_NoViewedPosts() {
89 // 模拟用户未浏览任何帖子
90 Long userId = 300L;
91 when(postViewMapper.findViewedPostIds(userId))
92 .thenReturn(Collections.emptyList());
93
94 // 模拟推荐结果(所有帖子按热度排序)
95 Post post1 = new Post().setPostId(1L).setHotScore(7.5);
96 Post post2 = new Post().setPostId(2L).setHotScore(9.0);
97 when(postMapper.selectPage(any(Page.class), any(QueryWrapper.class)))
98 .thenReturn(new Page<Post>().setRecords(Arrays.asList(post2, post1)));
99
100 // 调用推荐接口
101 Page<Post> result = postService.getRecommendedPosts(1, 10, userId);
102
103 // 验证结果
104 assertEquals(2, result.getRecords().size(), "应返回所有帖子");
105 assertEquals(2L, result.getRecords().get(0).getPostId(), "热度更高的帖子应排在前面");
106 }
107
108 @Test
109 public void testCalculateHotScores_UpdatesHotScoreCorrectly() {
110 // 设置存根
111 when(postMapper.selectList(any(QueryWrapper.class)))
112 .thenReturn(Collections.singletonList(mockPost));
113 when(postMapper.selectLikeCount(anyLong())).thenReturn(30L);
114 when(commentMapper.selectCountByPostId(anyLong())).thenReturn(20L);
115 when(postMapper.batchUpdateHotScore(anyList())).thenReturn(1);
116
117 // 执行并验证
118 postService.calculateHotScores();
119 double expectedScore = (Math.log(51) * 0.2 + 30 * 0.5 + 20 * 0.3) / Math.pow(4, 1.5);
120 assertEquals(expectedScore, mockPost.getHotScore(), 0.01);
121 verify(postMapper).batchUpdateHotScore(anyList());
122 }
123
124 //--------------------- 测试冷启动逻辑 ---------------------
125 @Test
126 public void testCreatePost_SetsInitialHotScore() {
127 Post newPost = new Post().setPostId(4L).setPostTitle("New Post");
128 postService.createPost(newPost);
129
130 assertEquals(5.0, newPost.getHotScore(), "新帖子的初始热度应为5.0");
131 assertNotNull(newPost.getCreatedAt(), "创建时间不应为空");
132 }
133 @Test
134 public void testConcurrentViewCountUpdate() {
135 // 设置存根
136 doNothing().when(postMapper).incrementViewCount(anyLong());
137
138 postService.recordViewHistory(100L, 1L);
139 postService.recordViewHistory(200L, 1L);
140
141 verify(postMapper, times(2)).incrementViewCount(1L);
142 verify(postViewMapper, times(2)).insert(any(PostView.class));
143 }
144}