Commit f640b67159208a0c6827574b7002e683cb0eb9b6

Authored by YunaiV
1 parent f2136c43

🐛 fix(im):codex 评审修复 IM 单测契约

- ImPrivateMessageServiceImplTest 用 getFriendState 替换已删除的 isFriend mock;返回 ImFriendStateEnum.FRIEND / NONE
- ImFriendServiceImplTest 删 stale 测试(isFriend / addFriend / deleteFriend 二参版本均已不存在);addFriend0 测试参数补齐 null displayName / addSource 凑齐 4 参签名
- 修复后 mvn -pl yudao-module-im test-compile 通过

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImplTest.java
1 package cn.iocoder.yudao.module.im.service.friend; 1 package cn.iocoder.yudao.module.im.service.friend;
2 2
3 -import cn.hutool.extra.spring.SpringUtil;  
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.exception.ServiceException; 4 import cn.iocoder.yudao.framework.common.exception.ServiceException;
6 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 5 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
@@ -15,21 +14,16 @@ import org.junit.jupiter.api.Test; @@ -15,21 +14,16 @@ import org.junit.jupiter.api.Test;
15 import org.mockito.ArgumentCaptor; 14 import org.mockito.ArgumentCaptor;
16 import org.mockito.InjectMocks; 15 import org.mockito.InjectMocks;
17 import org.mockito.Mock; 16 import org.mockito.Mock;
18 -import org.mockito.MockedStatic;  
19 import org.springframework.dao.DuplicateKeyException; 17 import org.springframework.dao.DuplicateKeyException;
20 18
21 import java.time.LocalDateTime; 19 import java.time.LocalDateTime;
22 import java.util.List; 20 import java.util.List;
23 21
24 -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_ADD_SELF;  
25 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_NOT_FRIEND; 22 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_NOT_FRIEND;
26 import static org.junit.jupiter.api.Assertions.*; 23 import static org.junit.jupiter.api.Assertions.*;
27 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.any;
28 -import static org.mockito.ArgumentMatchers.anyString;  
29 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.ArgumentMatchers.eq;
30 -import static org.mockito.Mockito.mockStatic;  
31 import static org.mockito.Mockito.never; 26 import static org.mockito.Mockito.never;
32 -import static org.mockito.Mockito.times;  
33 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when; 28 import static org.mockito.Mockito.when;
35 29
@@ -52,28 +46,6 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest { @@ -52,28 +46,6 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest {
52 @Mock 46 @Mock
53 private ImPrivateMessageService privateMessageService; 47 private ImPrivateMessageService privateMessageService;
54 48
55 - // ========== isFriend ==========  
56 -  
57 - @Test  
58 - public void testIsFriend_enabled() {  
59 - ImFriendDO friend = ImFriendDO.builder().userId(1L).friendUserId(2L)  
60 - .status(CommonStatusEnum.ENABLE.getStatus()).build();  
61 - when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(friend);  
62 -  
63 - assertTrue(friendService.isFriend(1L, 2L));  
64 - }  
65 -  
66 - @Test  
67 - public void testIsFriend_disabledOrAbsent() {  
68 - when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(null);  
69 - assertFalse(friendService.isFriend(1L, 2L));  
70 -  
71 - ImFriendDO disabled = ImFriendDO.builder().userId(1L).friendUserId(3L)  
72 - .status(CommonStatusEnum.DISABLE.getStatus()).build();  
73 - when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 3L)).thenReturn(disabled);  
74 - assertFalse(friendService.isFriend(1L, 3L));  
75 - }  
76 -  
77 // ========== updateFriend ========== 49 // ========== updateFriend ==========
78 50
79 @Test 51 @Test
@@ -152,36 +124,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest { @@ -152,36 +124,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest {
152 verify(imWebSocketService, never()).sendPrivateMessageAsync(any(Long.class), any(ImPrivateMessageDTO.class)); 124 verify(imWebSocketService, never()).sendPrivateMessageAsync(any(Long.class), any(ImPrivateMessageDTO.class));
153 } 125 }
154 126
155 - // ========== addFriend ==========  
156 -  
157 - @Test  
158 - public void testAddFriend_self() {  
159 - ServiceException exception = assertThrows(ServiceException.class,  
160 - () -> friendService.addFriend(1L, 1L));  
161 - assertEquals(FRIEND_ADD_SELF.getCode(), exception.getCode());  
162 - }  
163 -  
164 - @Test  
165 - public void testAddFriend_success() {  
166 - try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {  
167 - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImFriendServiceImpl.class)))  
168 - .thenReturn(friendService);  
169 -  
170 - // 准备  
171 - when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(null);  
172 - when(imFriendMapper.selectByUserIdAndFriendUserId(2L, 1L)).thenReturn(null);  
173 -  
174 - // 调用  
175 - friendService.addFriend(1L, 2L);  
176 -  
177 - // 断言:校验对方存在  
178 - verify(adminUserApi).validateUser(2L);  
179 - // 断言:双向插入 2 条好友关系  
180 - verify(imFriendMapper, times(2)).insert(any(ImFriendDO.class));  
181 - // 断言:FRIEND_ADD 单条入库(双向 WebSocket 推送由 sendPrivateMessage 内部完成,mock 下不可见)  
182 - verify(privateMessageService).sendPrivateMessage(eq(1L), any());  
183 - }  
184 - } 127 + // ========== addFriend0(内部方法,被 becomeFriends / silentReAddFriend 调用) ==========
185 128
186 @Test 129 @Test
187 public void testAddFriend0_existingEnabledSkipsUpdate() { 130 public void testAddFriend0_existingEnabledSkipsUpdate() {
@@ -191,7 +134,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest { @@ -191,7 +134,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest {
191 when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(exists); 134 when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(exists);
192 135
193 // 调用 136 // 调用
194 - friendService.addFriend0(1L, 2L); 137 + friendService.addFriend0(1L, 2L, null, null);
195 138
196 // 断言:不插入,也不更新 139 // 断言:不插入,也不更新
197 verify(imFriendMapper, never()).insert(any(ImFriendDO.class)); 140 verify(imFriendMapper, never()).insert(any(ImFriendDO.class));
@@ -206,7 +149,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest { @@ -206,7 +149,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest {
206 when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(exists); 149 when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(exists);
207 150
208 // 调用 151 // 调用
209 - friendService.addFriend0(1L, 2L); 152 + friendService.addFriend0(1L, 2L, null, null);
210 153
211 // 断言:更新 status 为 ENABLE 154 // 断言:更新 status 为 ENABLE
212 ArgumentCaptor<ImFriendDO> captor = ArgumentCaptor.forClass(ImFriendDO.class); 155 ArgumentCaptor<ImFriendDO> captor = ArgumentCaptor.forClass(ImFriendDO.class);
@@ -224,7 +167,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest { @@ -224,7 +167,7 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest {
224 .thenThrow(new DuplicateKeyException("concurrent insert")); 167 .thenThrow(new DuplicateKeyException("concurrent insert"));
225 168
226 // 调用:应被吞掉,不抛异常 169 // 调用:应被吞掉,不抛异常
227 - friendService.addFriend0(1L, 2L); 170 + friendService.addFriend0(1L, 2L, null, null);
228 171
229 verify(imFriendMapper).insert(any(ImFriendDO.class)); 172 verify(imFriendMapper).insert(any(ImFriendDO.class));
230 } 173 }
@@ -232,28 +175,6 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest { @@ -232,28 +175,6 @@ public class ImFriendServiceImplTest extends BaseMockitoUnitTest {
232 // ========== deleteFriend ========== 175 // ========== deleteFriend ==========
233 176
234 @Test 177 @Test
235 - public void testDeleteFriend_success() {  
236 - try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {  
237 - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImFriendServiceImpl.class)))  
238 - .thenReturn(friendService);  
239 -  
240 - ImFriendDO friend12 = ImFriendDO.builder().id(10L).userId(1L).friendUserId(2L)  
241 - .status(CommonStatusEnum.ENABLE.getStatus()).build();  
242 - ImFriendDO friend21 = ImFriendDO.builder().id(11L).userId(2L).friendUserId(1L)  
243 - .status(CommonStatusEnum.ENABLE.getStatus()).build();  
244 - when(imFriendMapper.selectByUserIdAndFriendUserId(1L, 2L)).thenReturn(friend12);  
245 - when(imFriendMapper.selectByUserIdAndFriendUserId(2L, 1L)).thenReturn(friend21);  
246 -  
247 - friendService.deleteFriend(1L, 2L);  
248 -  
249 - // 断言:双向更新为 DISABLE  
250 - verify(imFriendMapper, times(2)).updateById(any(ImFriendDO.class));  
251 - // 断言:仅给 userId 多端推送 FRIEND_DELETE 事件(friendUserId 不感知)  
252 - verify(imWebSocketService).sendPrivateMessageAsync(eq(1L), any(ImPrivateMessageDTO.class));  
253 - }  
254 - }  
255 -  
256 - @Test  
257 public void testDeleteFriend0_alreadyDisabled() { 178 public void testDeleteFriend0_alreadyDisabled() {
258 // 准备:已经是 DISABLE,不再更新 179 // 准备:已经是 DISABLE,不再更新
259 ImFriendDO exists = ImFriendDO.builder().id(10L).userId(1L).friendUserId(2L) 180 ImFriendDO exists = ImFriendDO.builder().id(10L).userId(1L).friendUserId(2L)
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivate @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivate
6 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO; 6 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO;
7 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; 7 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO;
8 import cn.iocoder.yudao.module.im.dal.mysql.message.ImPrivateMessageMapper; 8 import cn.iocoder.yudao.module.im.dal.mysql.message.ImPrivateMessageMapper;
  9 +import cn.iocoder.yudao.module.im.enums.friend.ImFriendStateEnum;
9 import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; 10 import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
10 import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; 11 import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum;
11 import cn.iocoder.yudao.module.im.service.friend.ImFriendService; 12 import cn.iocoder.yudao.module.im.service.friend.ImFriendService;
@@ -63,7 +64,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest { @@ -63,7 +64,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest {
63 ImPrivateMessageSendReqVO reqVO = buildSendReqVO(); 64 ImPrivateMessageSendReqVO reqVO = buildSendReqVO();
64 when(privateMessageMapper.selectBySenderIdAndClientMessageId(1L, "test-uuid-001")) 65 when(privateMessageMapper.selectBySenderIdAndClientMessageId(1L, "test-uuid-001"))
65 .thenReturn(null); 66 .thenReturn(null);
66 - when(friendService.isFriend(1L, 2L)).thenReturn(true); 67 + when(friendService.getFriendState(1L, 2L)).thenReturn(ImFriendStateEnum.FRIEND);
67 when(privateMessageMapper.insert(any(ImPrivateMessageDO.class))).thenAnswer(invocation -> { 68 when(privateMessageMapper.insert(any(ImPrivateMessageDO.class))).thenAnswer(invocation -> {
68 ImPrivateMessageDO msg = invocation.getArgument(0); 69 ImPrivateMessageDO msg = invocation.getArgument(0);
69 msg.setId(99L); 70 msg.setId(99L);
@@ -82,7 +83,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest { @@ -82,7 +83,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest {
82 assertNotNull(result.getSendTime()); 83 assertNotNull(result.getSendTime());
83 84
84 // 验证调用 85 // 验证调用
85 - verify(friendService).isFriend(1L, 2L); 86 + verify(friendService).getFriendState(1L, 2L);
86 verify(sensitiveWordService).validateText(reqVO.getContent()); 87 verify(sensitiveWordService).validateText(reqVO.getContent());
87 verify(privateMessageMapper).insert(any(ImPrivateMessageDO.class)); 88 verify(privateMessageMapper).insert(any(ImPrivateMessageDO.class));
88 // 验证推送给接收方和发送方 89 // 验证推送给接收方和发送方
@@ -116,7 +117,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest { @@ -116,7 +117,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest {
116 ImPrivateMessageSendReqVO reqVO = buildSendReqVO(); 117 ImPrivateMessageSendReqVO reqVO = buildSendReqVO();
117 when(privateMessageMapper.selectBySenderIdAndClientMessageId(1L, "test-uuid-001")) 118 when(privateMessageMapper.selectBySenderIdAndClientMessageId(1L, "test-uuid-001"))
118 .thenReturn(null); 119 .thenReturn(null);
119 - when(friendService.isFriend(1L, 2L)).thenReturn(false); 120 + when(friendService.getFriendState(1L, 2L)).thenReturn(ImFriendStateEnum.NONE);
120 121
121 // 调用并断言 122 // 调用并断言
122 ServiceException exception = assertThrows(ServiceException.class, 123 ServiceException exception = assertThrows(ServiceException.class,
@@ -279,7 +280,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest { @@ -279,7 +280,7 @@ public class ImPrivateMessageServiceImplTest extends BaseMockitoUnitTest {
279 ImPrivateMessageSendReqVO reqVO = buildSendReqVO(); 280 ImPrivateMessageSendReqVO reqVO = buildSendReqVO();
280 when(privateMessageMapper.selectBySenderIdAndClientMessageId(1L, "test-uuid-001")) 281 when(privateMessageMapper.selectBySenderIdAndClientMessageId(1L, "test-uuid-001"))
281 .thenReturn(null); 282 .thenReturn(null);
282 - when(friendService.isFriend(1L, 2L)).thenReturn(true); 283 + when(friendService.getFriendState(1L, 2L)).thenReturn(ImFriendStateEnum.FRIEND);
283 doThrow(new ServiceException(MESSAGE_SENSITIVE_WORD_BLOCKED)) 284 doThrow(new ServiceException(MESSAGE_SENSITIVE_WORD_BLOCKED))
284 .when(sensitiveWordService).validateText(reqVO.getContent()); 285 .when(sensitiveWordService).validateText(reqVO.getContent());
285 286