完善 AdminAuthServiceImpl 单元测试

This commit is contained in:
YunaiV 2023-01-31 22:43:39 +08:00
parent c107095d22
commit 2edf53a403
2 changed files with 238 additions and 108 deletions

View File

@ -57,10 +57,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
private SocialUserService socialUserService;
@Resource
private MemberService memberService;
@Resource
private Validator validator;
@Resource
private CaptchaService captchaService;
@Resource
@ -95,8 +93,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@Override
public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
// 判断验证码是否正确
verifyCaptcha(reqVO);
// 校验验证码
validateCaptcha(reqVO);
// 使用账号密码进行登录
AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
@ -106,7 +104,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
}
// 创建 Token 令牌记录登录日志
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
}
@ -136,25 +133,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
}
@VisibleForTesting
void verifyCaptcha(AuthLoginReqVO reqVO) {
// 如果验证码关闭则不进行校验
if (!captchaEnable) {
return;
}
// 校验验证码
ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
ResponseModel response = captchaService.verification(captchaVO);
// 验证不通过
if (!response.isSuccess()) {
// 创建登录失败日志验证码不正确)
createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR);
throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg());
}
}
private void createLoginLog(Long userId, String username,
LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) {
// 插入登录日志
@ -193,10 +171,23 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
}
@Override
public AuthLoginRespVO refreshToken(String refreshToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
return AuthConvert.INSTANCE.convert(accessTokenDO);
@VisibleForTesting
void validateCaptcha(AuthLoginReqVO reqVO) {
// 如果验证码关闭则不进行校验
if (!captchaEnable) {
return;
}
// 校验验证码
ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
ResponseModel response = captchaService.verification(captchaVO);
// 验证不通过
if (!response.isSuccess()) {
// 创建登录失败日志验证码不正确)
createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR);
throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg());
}
}
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
@ -209,6 +200,12 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return AuthConvert.INSTANCE.convert(accessTokenDO);
}
@Override
public AuthLoginRespVO refreshToken(String refreshToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
return AuthConvert.INSTANCE.convert(accessTokenDO);
}
@Override
public void logout(String token, Integer logType) {
// 删除访问令牌

View File

@ -1,31 +1,43 @@
package cn.iocoder.yudao.module.system.service.auth;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.member.MemberService;
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.xingyuv.captcha.model.common.ResponseModel;
import com.xingyuv.captcha.service.CaptchaService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@ -42,17 +54,24 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
@MockBean
private LoginLogService loginLogService;
@MockBean
private SocialUserService socialService;
private SocialUserService socialUserService;
@MockBean
private SmsCodeApi smsCodeApi;
@MockBean
private OAuth2TokenService oauth2TokenService;
@MockBean
private MemberService memberService;
@MockBean
private Validator validator;
@BeforeEach
public void setUp() {
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// 注入一个 Validator 对象
ReflectUtil.setFieldValue(authService, "validator",
Validation.buildDefaultValidatorFactory().getValidator());
}
@Test
public void testAuthenticate_success() {
// 准备参数
@ -78,7 +97,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
String password = randomString();
// 调用, 并断言异常
AssertUtils.assertServiceException(() -> authService.authenticate(username, password),
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_BAD_CREDENTIALS);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
@ -98,7 +117,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// 调用, 并断言异常
AssertUtils.assertServiceException(() -> authService.authenticate(username, password),
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_BAD_CREDENTIALS);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
@ -120,7 +139,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
// 调用, 并断言异常
AssertUtils.assertServiceException(() -> authService.authenticate(username, password),
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_USER_DISABLED);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
@ -129,82 +148,194 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
);
}
// @Test
// public void testCaptcha_success() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
//
// // mock 验证码正确
// when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
//
// // 调用
// authService.verifyCaptcha(reqVO);
// // 断言
// verify(captchaService).deleteCaptchaCode(reqVO.getUuid());
// }
//
// @Test
// public void testCaptcha_notFound() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
//
// // 调用, 并断言异常
// assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_NOT_FOUND);
// // 校验调用参数
// verify(loginLogService, times(1)).createLoginLog(
// argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
// && o.getResult().equals(LoginResultEnum.CAPTCHA_NOT_FOUND.getResult()))
// );
// }
@Test
public void testLogin_success() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
o.setUsername("test_username").setPassword("test_password")
.setSocialType(randomEle(SocialTypeEnum.values()).getType()));
// @Test
// public void testCaptcha_codeError() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
//
// // mock 验证码不正确
// String code = randomString();
// when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
//
// // 调用, 并断言异常
// assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
// // 校验调用参数
// verify(loginLogService).createLoginLog(
// argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
// && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
// );
// }
// mock 验证码正确
ReflectUtil.setFieldValue(authService, "captchaEnable", false);
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username")
.setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByUsername(eq("test_username"))).thenReturn(user);
// mock password 匹配
when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
.thenReturn(accessTokenDO);
// @Test
// public void testLogin_success() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
// o.setUsername("test_username").setPassword("test_password"));
//
// // mock 验证码正确
// when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// // mock user 数据
// AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username")
// .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus()));
// when(userService.getUserByUsername(eq("test_username"))).thenReturn(user);
// // mock password 匹配
// when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
// // mock 缓存登录用户到 Redis
// OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
// .setUserType(UserTypeEnum.ADMIN.getValue()));
// when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
// .thenReturn(accessTokenDO);
//
// // 调用, 并断言异常
// AuthLoginRespVO loginRespVO = authService.login(reqVO);
// assertPojoEquals(accessTokenDO, loginRespVO);
// // 校验调用参数
// verify(loginLogService).createLoginLog(
// argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
// && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
// && o.getUserId().equals(user.getId()))
// );
// }
// 调用并校验
AuthLoginRespVO loginRespVO = authService.login(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
&& o.getUserId().equals(user.getId()))
);
verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO(
user.getId(), UserTypeEnum.ADMIN.getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())));
}
@Test
public void testSendSmsCode() {
// 准备参数
String mobile = randomString();
Integer scene = randomEle(SmsSceneEnum.values()).getScene();
AuthSmsSendReqVO reqVO = new AuthSmsSendReqVO(mobile, scene);
// mock 方法用户信息
AdminUserDO user = randomPojo(AdminUserDO.class);
when(userService.getUserByMobile(eq(mobile))).thenReturn(user);
// 调用
authService.sendSmsCode(reqVO);
// 断言
verify(smsCodeApi).sendSmsCode(argThat(sendReqDTO -> {
assertEquals(mobile, sendReqDTO.getMobile());
assertEquals(scene, sendReqDTO.getScene());
return true;
}));
}
@Test
public void testSmsLogin_success() {
// 准备参数
String mobile = randomString();
String scene = randomString();
AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(mobile, scene);
// mock 方法用户信息
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L));
when(userService.getUserByMobile(eq(mobile))).thenReturn(user);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
.thenReturn(accessTokenDO);
// 调用并断言
AuthLoginRespVO loginRespVO = authService.smsLogin(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
// 断言调用
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_MOBILE.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testSocialLogin_success() {
// 准备参数
AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
// mock 方法绑定的用户编号
Long userId = 1L;
when(socialUserService.getBindUserId(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(userId);
// mock用户
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
when(userService.getUser(eq(userId))).thenReturn(user);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
.thenReturn(accessTokenDO);
// 调用并断言
AuthLoginRespVO loginRespVO = authService.socialLogin(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
// 断言调用
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_SOCIAL.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testValidateCaptcha_successWithEnable() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码打开
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// mock 验证通过
when(captchaService.verification(argThat(captchaVO -> {
assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
return true;
}))).thenReturn(ResponseModel.success());
// 调用无需断言
authService.validateCaptcha(reqVO);
}
@Test
public void testValidateCaptcha_successWithDisable() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码关闭
ReflectUtil.setFieldValue(authService, "captchaEnable", false);
// 调用无需断言
authService.validateCaptcha(reqVO);
}
@Test
public void testValidateCaptcha_constraintViolationException() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class).setCaptchaVerification(null);
// mock 验证码打开
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// 调用并断言异常
assertThrows(ConstraintViolationException.class, () -> authService.validateCaptcha(reqVO),
"验证码不能为空");
}
@Test
public void testCaptcha_fail() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码打开
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// mock 验证通过
when(captchaService.verification(argThat(captchaVO -> {
assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
return true;
}))).thenReturn(ResponseModel.errorMsg("就是不对"));
// 调用, 并断言异常
assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对");
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
);
}
@Test
public void testRefreshToken() {
// 准备参数
String refreshToken = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq("default")))
.thenReturn(accessTokenDO);
// 调用
AuthLoginRespVO loginRespVO = authService.refreshToken(refreshToken);
// 断言
assertPojoEquals(accessTokenDO, loginRespVO);
}
@Test
public void testLogout_success() {
@ -221,6 +352,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
);
// 调用并校验
}
@Test