package com.iailab.module.system.service.auth;
|
|
import cn.hutool.core.util.ReflectUtil;
|
import com.iailab.framework.common.enums.CommonStatusEnum;
|
import com.iailab.framework.common.enums.UserTypeEnum;
|
import com.iailab.framework.common.pojo.CommonResult;
|
import com.iailab.framework.test.core.ut.BaseDbUnitTest;
|
import com.iailab.module.system.api.sms.SmsCodeApi;
|
import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO;
|
import com.iailab.module.system.api.social.dto.SocialUserRespDTO;
|
import com.iailab.module.system.controller.admin.auth.vo.*;
|
import com.iailab.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
import com.iailab.module.system.dal.dataobject.user.AdminUserDO;
|
import com.iailab.module.system.enums.logger.LoginLogTypeEnum;
|
import com.iailab.module.system.enums.logger.LoginResultEnum;
|
import com.iailab.module.system.enums.sms.SmsSceneEnum;
|
import com.iailab.module.system.enums.social.SocialTypeEnum;
|
import com.iailab.module.system.service.logger.LoginLogService;
|
import com.iailab.module.system.service.member.MemberService;
|
import com.iailab.module.system.service.oauth2.OAuth2TokenService;
|
import com.iailab.module.system.service.social.SocialUserService;
|
import com.iailab.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 com.iailab.framework.common.pojo.CommonResult.success;
|
import static com.iailab.framework.test.core.util.AssertUtils.assertPojoEquals;
|
import static com.iailab.framework.test.core.util.AssertUtils.assertServiceException;
|
import static com.iailab.framework.test.core.util.RandomUtils.randomPojo;
|
import static com.iailab.framework.test.core.util.RandomUtils.randomString;
|
import static com.iailab.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.*;
|
|
@Import(AdminAuthServiceImpl.class)
|
public class AdminAuthServiceImplTest extends BaseDbUnitTest {
|
|
@Resource
|
private AdminAuthServiceImpl authService;
|
|
@MockBean
|
private AdminUserService userService;
|
@MockBean
|
private CaptchaService captchaService;
|
@MockBean
|
private LoginLogService loginLogService;
|
@MockBean
|
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() {
|
// 准备参数
|
String username = randomString();
|
String password = randomString();
|
// mock user 数据
|
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
|
.setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
when(userService.getUserByUsername(eq(username))).thenReturn(user);
|
// mock password 匹配
|
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
|
|
// 调用
|
AdminUserDO loginUser = authService.authenticate(username, password);
|
// 校验
|
assertPojoEquals(user, loginUser);
|
}
|
|
@Test
|
public void testAuthenticate_userNotFound() {
|
// 准备参数
|
String username = randomString();
|
String password = randomString();
|
|
// 调用, 并断言异常
|
assertServiceException(() -> authService.authenticate(username, password),
|
AUTH_LOGIN_BAD_CREDENTIALS);
|
verify(loginLogService).createLoginLog(
|
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
|
&& o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
|
&& o.getUserId() == null)
|
);
|
}
|
|
@Test
|
public void testAuthenticate_badCredentials() {
|
// 准备参数
|
String username = randomString();
|
String password = randomString();
|
// mock user 数据
|
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
|
.setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
when(userService.getUserByUsername(eq(username))).thenReturn(user);
|
|
// 调用, 并断言异常
|
assertServiceException(() -> authService.authenticate(username, password),
|
AUTH_LOGIN_BAD_CREDENTIALS);
|
verify(loginLogService).createLoginLog(
|
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
|
&& o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
|
&& o.getUserId().equals(user.getId()))
|
);
|
}
|
|
@Test
|
public void testAuthenticate_userDisabled() {
|
// 准备参数
|
String username = randomString();
|
String password = randomString();
|
// mock user 数据
|
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
|
.setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
when(userService.getUserByUsername(eq(username))).thenReturn(user);
|
// mock password 匹配
|
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
|
|
// 调用, 并断言异常
|
assertServiceException(() -> authService.authenticate(username, password),
|
AUTH_LOGIN_USER_DISABLED);
|
verify(loginLogService).createLoginLog(
|
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
|
&& o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())
|
&& o.getUserId().equals(user.getId()))
|
);
|
}
|
|
@Test
|
public void testLogin_success() {
|
// 准备参数
|
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
|
o.setUsername("test_username").setPassword("test_password")
|
.setSocialType(randomEle(SocialTypeEnum.values()).getType()));
|
|
// 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);
|
|
// 调用,并校验
|
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 code = randomString();
|
AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(mobile, code);
|
// mock 方法(校验验证码)
|
when(smsCodeApi.useSmsCode(argThat(reqDTO -> {
|
assertEquals(mobile, reqDTO.getMobile());
|
assertEquals(code, reqDTO.getCode());
|
assertEquals(SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), reqDTO.getScene());
|
return true;
|
}))).thenReturn(success(true));
|
// 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.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
|
eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), 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() {
|
// 准备参数
|
String token = randomString();
|
// mock
|
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
|
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO);
|
|
// 调用
|
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
|
// 校验调用参数
|
verify(loginLogService).createLoginLog(
|
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
|
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
|
);
|
// 调用,并校验
|
|
}
|
|
@Test
|
public void testLogout_fail() {
|
// 准备参数
|
String token = randomString();
|
|
// 调用
|
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
|
// 校验调用参数
|
verify(loginLogService, never()).createLoginLog(any());
|
}
|
|
}
|