houzhongyi
2024-07-11 e7c1260db32209a078a962aaa0ad5492c35774fb
提交 | 用户 | 时间
e7c126 1 package com.iailab.module.system.service.auth;
H 2
3 import cn.hutool.core.util.ReflectUtil;
4 import com.iailab.framework.common.enums.CommonStatusEnum;
5 import com.iailab.framework.common.enums.UserTypeEnum;
6 import com.iailab.framework.common.pojo.CommonResult;
7 import com.iailab.framework.test.core.ut.BaseDbUnitTest;
8 import com.iailab.module.system.api.sms.SmsCodeApi;
9 import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO;
10 import com.iailab.module.system.api.social.dto.SocialUserRespDTO;
11 import com.iailab.module.system.controller.admin.auth.vo.*;
12 import com.iailab.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
13 import com.iailab.module.system.dal.dataobject.user.AdminUserDO;
14 import com.iailab.module.system.enums.logger.LoginLogTypeEnum;
15 import com.iailab.module.system.enums.logger.LoginResultEnum;
16 import com.iailab.module.system.enums.sms.SmsSceneEnum;
17 import com.iailab.module.system.enums.social.SocialTypeEnum;
18 import com.iailab.module.system.service.logger.LoginLogService;
19 import com.iailab.module.system.service.member.MemberService;
20 import com.iailab.module.system.service.oauth2.OAuth2TokenService;
21 import com.iailab.module.system.service.social.SocialUserService;
22 import com.iailab.module.system.service.user.AdminUserService;
23 import com.xingyuv.captcha.model.common.ResponseModel;
24 import com.xingyuv.captcha.service.CaptchaService;
25 import org.junit.jupiter.api.BeforeEach;
26 import org.junit.jupiter.api.Test;
27 import org.springframework.boot.test.mock.mockito.MockBean;
28 import org.springframework.context.annotation.Import;
29
30 import javax.annotation.Resource;
31 import javax.validation.ConstraintViolationException;
32 import javax.validation.Validation;
33 import javax.validation.Validator;
34
35 import static cn.hutool.core.util.RandomUtil.randomEle;
36 import static com.iailab.framework.common.pojo.CommonResult.success;
37 import static com.iailab.framework.test.core.util.AssertUtils.assertPojoEquals;
38 import static com.iailab.framework.test.core.util.AssertUtils.assertServiceException;
39 import static com.iailab.framework.test.core.util.RandomUtils.randomPojo;
40 import static com.iailab.framework.test.core.util.RandomUtils.randomString;
41 import static com.iailab.module.system.enums.ErrorCodeConstants.*;
42 import static org.junit.jupiter.api.Assertions.assertEquals;
43 import static org.junit.jupiter.api.Assertions.assertThrows;
44 import static org.mockito.ArgumentMatchers.eq;
45 import static org.mockito.Mockito.*;
46
47 @Import(AdminAuthServiceImpl.class)
48 public class AdminAuthServiceImplTest extends BaseDbUnitTest {
49
50     @Resource
51     private AdminAuthServiceImpl authService;
52
53     @MockBean
54     private AdminUserService userService;
55     @MockBean
56     private CaptchaService captchaService;
57     @MockBean
58     private LoginLogService loginLogService;
59     @MockBean
60     private SocialUserService socialUserService;
61     @MockBean
62     private SmsCodeApi smsCodeApi;
63     @MockBean
64     private OAuth2TokenService oauth2TokenService;
65     @MockBean
66     private MemberService memberService;
67     @MockBean
68     private Validator validator;
69
70     @BeforeEach
71     public void setUp() {
72         ReflectUtil.setFieldValue(authService, "captchaEnable", true);
73         // 注入一个 Validator 对象
74         ReflectUtil.setFieldValue(authService, "validator",
75                 Validation.buildDefaultValidatorFactory().getValidator());
76     }
77
78     @Test
79     public void testAuthenticate_success() {
80         // 准备参数
81         String username = randomString();
82         String password = randomString();
83         // mock user 数据
84         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
85                 .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
86         when(userService.getUserByUsername(eq(username))).thenReturn(user);
87         // mock password 匹配
88         when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
89
90         // 调用
91         AdminUserDO loginUser = authService.authenticate(username, password);
92         // 校验
93         assertPojoEquals(user, loginUser);
94     }
95
96     @Test
97     public void testAuthenticate_userNotFound() {
98         // 准备参数
99         String username = randomString();
100         String password = randomString();
101
102         // 调用, 并断言异常
103         assertServiceException(() -> authService.authenticate(username, password),
104                 AUTH_LOGIN_BAD_CREDENTIALS);
105         verify(loginLogService).createLoginLog(
106                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
107                         && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
108                         && o.getUserId() == null)
109         );
110     }
111
112     @Test
113     public void testAuthenticate_badCredentials() {
114         // 准备参数
115         String username = randomString();
116         String password = randomString();
117         // mock user 数据
118         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
119                 .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
120         when(userService.getUserByUsername(eq(username))).thenReturn(user);
121
122         // 调用, 并断言异常
123         assertServiceException(() -> authService.authenticate(username, password),
124                 AUTH_LOGIN_BAD_CREDENTIALS);
125         verify(loginLogService).createLoginLog(
126                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
127                         && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
128                         && o.getUserId().equals(user.getId()))
129         );
130     }
131
132     @Test
133     public void testAuthenticate_userDisabled() {
134         // 准备参数
135         String username = randomString();
136         String password = randomString();
137         // mock user 数据
138         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
139                 .setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus()));
140         when(userService.getUserByUsername(eq(username))).thenReturn(user);
141         // mock password 匹配
142         when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
143
144         // 调用, 并断言异常
145         assertServiceException(() -> authService.authenticate(username, password),
146                 AUTH_LOGIN_USER_DISABLED);
147         verify(loginLogService).createLoginLog(
148                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
149                         && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())
150                         && o.getUserId().equals(user.getId()))
151         );
152     }
153
154     @Test
155     public void testLogin_success() {
156         // 准备参数
157         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
158                 o.setUsername("test_username").setPassword("test_password")
159                         .setSocialType(randomEle(SocialTypeEnum.values()).getType()));
160
161         // mock 验证码正确
162         ReflectUtil.setFieldValue(authService, "captchaEnable", false);
163         // mock user 数据
164         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username")
165                 .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus()));
166         when(userService.getUserByUsername(eq("test_username"))).thenReturn(user);
167         // mock password 匹配
168         when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
169         // mock 缓存登录用户到 Redis
170         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
171                 .setUserType(UserTypeEnum.ADMIN.getValue()));
172         when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
173                 .thenReturn(accessTokenDO);
174
175         // 调用,并校验
176         AuthLoginRespVO loginRespVO = authService.login(reqVO);
177         assertPojoEquals(accessTokenDO, loginRespVO);
178         // 校验调用参数
179         verify(loginLogService).createLoginLog(
180                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
181                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
182                         && o.getUserId().equals(user.getId()))
183         );
184         verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO(
185                 user.getId(), UserTypeEnum.ADMIN.getValue(),
186                 reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())));
187     }
188
189     @Test
190     public void testSendSmsCode() {
191         // 准备参数
192         String mobile = randomString();
193         Integer scene = randomEle(SmsSceneEnum.values()).getScene();
194         AuthSmsSendReqVO reqVO = new AuthSmsSendReqVO(mobile, scene);
195         // mock 方法(用户信息)
196         AdminUserDO user = randomPojo(AdminUserDO.class);
197         when(userService.getUserByMobile(eq(mobile))).thenReturn(user);
198
199         // 调用
200         authService.sendSmsCode(reqVO);
201         // 断言
202         verify(smsCodeApi).sendSmsCode(argThat(sendReqDTO -> {
203             assertEquals(mobile, sendReqDTO.getMobile());
204             assertEquals(scene, sendReqDTO.getScene());
205             return true;
206         }));
207     }
208
209     @Test
210     public void testSmsLogin_success() {
211         // 准备参数
212         String mobile = randomString();
213         String code = randomString();
214         AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(mobile, code);
215         // mock 方法(校验验证码)
216         when(smsCodeApi.useSmsCode(argThat(reqDTO -> {
217             assertEquals(mobile, reqDTO.getMobile());
218             assertEquals(code, reqDTO.getCode());
219             assertEquals(SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), reqDTO.getScene());
220             return true;
221         }))).thenReturn(success(true));
222         // mock 方法(用户信息)
223         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L));
224         when(userService.getUserByMobile(eq(mobile))).thenReturn(user);
225         // mock 缓存登录用户到 Redis
226         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
227                 .setUserType(UserTypeEnum.ADMIN.getValue()));
228         when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
229                 .thenReturn(accessTokenDO);
230
231         // 调用,并断言
232         AuthLoginRespVO loginRespVO = authService.smsLogin(reqVO);
233         assertPojoEquals(accessTokenDO, loginRespVO);
234         // 断言调用
235         verify(loginLogService).createLoginLog(
236                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_MOBILE.getType())
237                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
238                         && o.getUserId().equals(user.getId()))
239         );
240     }
241
242     @Test
243     public void testSocialLogin_success() {
244         // 准备参数
245         AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
246         // mock 方法(绑定的用户编号)
247         Long userId = 1L;
248         when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
249                 eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId));
250         // mock(用户)
251         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
252         when(userService.getUser(eq(userId))).thenReturn(user);
253         // mock 缓存登录用户到 Redis
254         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
255                 .setUserType(UserTypeEnum.ADMIN.getValue()));
256         when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
257                 .thenReturn(accessTokenDO);
258
259         // 调用,并断言
260         AuthLoginRespVO loginRespVO = authService.socialLogin(reqVO);
261         assertPojoEquals(accessTokenDO, loginRespVO);
262         // 断言调用
263         verify(loginLogService).createLoginLog(
264                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_SOCIAL.getType())
265                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
266                         && o.getUserId().equals(user.getId()))
267         );
268     }
269
270     @Test
271     public void testValidateCaptcha_successWithEnable() {
272         // 准备参数
273         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
274
275         // mock 验证码打开
276         ReflectUtil.setFieldValue(authService, "captchaEnable", true);
277         // mock 验证通过
278         when(captchaService.verification(argThat(captchaVO -> {
279             assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
280             return true;
281         }))).thenReturn(ResponseModel.success());
282
283         // 调用,无需断言
284         authService.validateCaptcha(reqVO);
285     }
286
287     @Test
288     public void testValidateCaptcha_successWithDisable() {
289         // 准备参数
290         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
291
292         // mock 验证码关闭
293         ReflectUtil.setFieldValue(authService, "captchaEnable", false);
294
295         // 调用,无需断言
296         authService.validateCaptcha(reqVO);
297     }
298
299     @Test
300     public void testValidateCaptcha_constraintViolationException() {
301         // 准备参数
302         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class).setCaptchaVerification(null);
303
304         // mock 验证码打开
305         ReflectUtil.setFieldValue(authService, "captchaEnable", true);
306
307         // 调用,并断言异常
308         assertThrows(ConstraintViolationException.class, () -> authService.validateCaptcha(reqVO),
309                 "验证码不能为空");
310     }
311
312
313     @Test
314     public void testCaptcha_fail() {
315         // 准备参数
316         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
317
318         // mock 验证码打开
319         ReflectUtil.setFieldValue(authService, "captchaEnable", true);
320         // mock 验证通过
321         when(captchaService.verification(argThat(captchaVO -> {
322             assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
323             return true;
324         }))).thenReturn(ResponseModel.errorMsg("就是不对"));
325
326         // 调用, 并断言异常
327         assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对");
328         // 校验调用参数
329         verify(loginLogService).createLoginLog(
330                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
331                         && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
332         );
333     }
334
335     @Test
336     public void testRefreshToken() {
337         // 准备参数
338         String refreshToken = randomString();
339         // mock 方法
340         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
341         when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq("default")))
342                 .thenReturn(accessTokenDO);
343
344         // 调用
345         AuthLoginRespVO loginRespVO = authService.refreshToken(refreshToken);
346         // 断言
347         assertPojoEquals(accessTokenDO, loginRespVO);
348     }
349
350     @Test
351     public void testLogout_success() {
352         // 准备参数
353         String token = randomString();
354         // mock
355         OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
356                 .setUserType(UserTypeEnum.ADMIN.getValue()));
357         when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO);
358
359         // 调用
360         authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
361         // 校验调用参数
362         verify(loginLogService).createLoginLog(
363                 argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
364                         && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
365         );
366         // 调用,并校验
367
368     }
369
370     @Test
371     public void testLogout_fail() {
372         // 准备参数
373         String token = randomString();
374
375         // 调用
376         authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
377         // 校验调用参数
378         verify(loginLogService, never()).createLoginLog(any());
379     }
380
381 }