Merge branch 'upstream/master'

# Conflicts:
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/UserPostDO.java
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/UserPostMapper.java
This commit is contained in:
anzhen
2022-04-27 22:17:43 +08:00
1067 changed files with 14352 additions and 12149 deletions

View File

@ -0,0 +1,278 @@
package cn.iocoder.yudao.module.system.service.auth;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginReqVO;
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.service.common.CaptchaService;
import cn.iocoder.yudao.module.system.service.dept.PostService;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.Set;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static java.util.Collections.singleton;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import(AdminAuthServiceImpl.class)
public class AuthServiceImplTest extends BaseDbUnitTest {
@Resource
private AdminAuthServiceImpl authService;
@MockBean
private AdminUserService userService;
@MockBean
private PermissionService permissionService;
@MockBean
private AuthenticationManager authenticationManager;
@MockBean
private Authentication authentication;
@MockBean
private CaptchaService captchaService;
@MockBean
private LoginLogService loginLogService;
@MockBean
private UserSessionService userSessionService;
@MockBean
private SocialUserService socialService;
@MockBean
private PostService postService;
@MockBean
private Validator validator;
@BeforeEach
public void setUp() {
when(captchaService.isCaptchaEnable()).thenReturn(true);
}
@Test
public void testLoadUserByUsername_success() {
// 准备参数
String username = randomString();
// mock 方法
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// 调用
LoginUser loginUser = (LoginUser) authService.loadUserByUsername(username);
// 校验
AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
}
@Test
public void testLoadUserByUsername_userNotFound() {
// 准备参数
String username = randomString();
// mock 方法
// 调用, 并断言异常
assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
() -> authService.loadUserByUsername(username),
username); // 异常提示为 username
}
@Test
public void testMockLogin_success() {
// 准备参数
Long userId = randomLongId();
// mock 方法 01
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
when(userService.getUser(eq(userId))).thenReturn(user);
// mock 方法 02
Set<Long> roleIds = randomSet(Long.class);
when(permissionService.getUserRoleIds(eq(userId), eq(singleton(CommonStatusEnum.ENABLE.getStatus()))))
.thenReturn(roleIds);
// 调用
LoginUser loginUser = authService.mockLogin(userId);
// 断言
AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
assertEquals(roleIds, loginUser.getRoleIds());
}
@Test
public void testMockLogin_userNotFound() {
// 准备参数
Long userId = randomLongId();
// mock 方法
// 调用, 并断言异常
assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
() -> authService.mockLogin(userId),
String.valueOf(userId)); // 异常提示为 userId
}
@Test
public void testLogin_captchaNotFound() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
String userIp = randomString();
String userAgent = randomString();
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), 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_captchaCodeError() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
String code = randomString();
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码不正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
// 校验调用参数
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
);
}
@Test
public void testLogin_badCredentials() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock 抛出异常
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenThrow(new BadCredentialsException("测试账号或密码不正确"));
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()))
);
}
@Test
public void testLogin_userDisabled() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock 抛出异常
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenThrow(new DisabledException("测试用户被禁用"));
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult()))
);
}
@Test
public void testLogin_unknownError() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock 抛出异常
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenThrow(new AuthenticationException("测试未知异常") {});
// 调用, 并断言异常
assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.UNKNOWN_ERROR.getResult()))
);
}
@Test
public void testLogin_success() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
Long userId = randomLongId();
Set<Long> userRoleIds = randomSet(Long.class);
String sessionId = randomString();
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
LoginUser loginUser = randomPojo(LoginUser.class, o -> {
o.setId(userId);
o.setRoleIds(userRoleIds);
});
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock authentication
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenReturn(authentication);
when(authentication.getPrincipal()).thenReturn(loginUser);
// mock 获得 User 拥有的角色编号数组
when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds);
// mock 缓存登录用户到 Redis
when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(sessionId);
// 调用, 并断言异常
String login = authService.login(reqVO, userIp, userAgent);
assertEquals(sessionId, login);
// 校验调用参数
verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
);
}
@Test
public void testLogout_success() {
// 准备参数
String token = randomString();
LoginUser loginUser = randomPojo(LoginUser.class);
// mock
when(userSessionService.getLoginUser(token)).thenReturn(loginUser);
// 调用
authService.logout(token);
// 校验调用参数
verify(userSessionService, times(1)).deleteUserSession(token);
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
);
}
}

View File

@ -0,0 +1,219 @@
package cn.iocoder.yudao.module.system.service.auth;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
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 java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link UserSessionServiceImpl} 的单元测试
*
* @author Lyon
*/
@Import({UserSessionServiceImpl.class, LoginUserRedisDAO.class})
public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource
private UserSessionServiceImpl userSessionService;
@Resource
private UserSessionMapper userSessionMapper;
@MockBean
private AdminUserService userService;
@MockBean
private LoginLogService loginLogService;
@Resource
private LoginUserRedisDAO loginUserRedisDAO;
@MockBean
private SecurityProperties securityProperties;
@Test
public void testGetUserSessionPage_success() {
// mock 数据
AdminUserDO dbUser = randomPojo(AdminUserDO.class, o -> {
o.setSex(randomEle(SexEnum.values()).getSex());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(userService.getUsersByUsername(eq(dbUser.getUsername()))).thenReturn(singletonList(dbUser));
// 插入可被查询到的数据
String userIp = randomString();
UserSessionDO dbSession = randomPojo(UserSessionDO.class, o -> {
o.setUserId(dbUser.getId());
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setUserIp(userIp);
});
userSessionMapper.insert(dbSession);
// 测试 username 不匹配
userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> {
o.setId(randomString());
o.setUserId(123456L);
}));
// 测试 userIp 不匹配
userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> {
o.setId(randomString());
o.setUserIp("testUserIp");
}));
// 准备参数
UserSessionPageReqVO reqVO = new UserSessionPageReqVO();
reqVO.setUsername(dbUser.getUsername());
reqVO.setUserIp(userIp);
// 调用
PageResult<UserSessionDO> pageResult = userSessionService.getUserSessionPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSession, pageResult.getList().get(0));
}
// TODO 芋艿:单测写的有问题
@Test
public void testClearSessionTimeout_success() {
// 准备超时数据 120 条, 在线用户 1 条
int expectedTimeoutCount = 120, expectedTotal = 1;
// 准备数据
List<UserSessionDO> prepareData = Stream
.iterate(0, i -> i)
.limit(expectedTimeoutCount)
.map(i -> randomPojo(UserSessionDO.class, o -> {
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setSessionTimeout(DateUtil.offsetSecond(new Date(), -1));
}))
.collect(Collectors.toList());
UserSessionDO sessionDO = randomPojo(UserSessionDO.class, o -> {
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setSessionTimeout(DateUtil.offsetMinute(new Date(), 30));
});
prepareData.add(sessionDO);
prepareData.forEach(userSessionMapper::insert);
// 清空超时数据
long actualTimeoutCount = userSessionService.clearSessionTimeout();
// 校验
assertEquals(expectedTimeoutCount, actualTimeoutCount);
List<UserSessionDO> userSessionDOS = userSessionMapper.selectList();
assertEquals(expectedTotal, userSessionDOS.size());
assertPojoEquals(sessionDO, userSessionDOS.get(0), "updateTime");
}
@Test
public void testCreateUserSession_success() {
// 准备参数
String userIp = randomString();
String userAgent = randomString();
LoginUser loginUser = randomPojo(LoginUser.class, o -> {
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setTenantId(0L); // 租户设置为 0因为暂未启用多租户组件
});
// mock 方法
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
// 调用
String sessionId = userSessionService.createUserSession(loginUser, userIp, userAgent);
// 校验 UserSessionDO 记录
UserSessionDO userSessionDO = userSessionMapper.selectById(sessionId);
assertPojoEquals(loginUser, userSessionDO, "id", "updateTime");
assertEquals(sessionId, userSessionDO.getId());
assertEquals(userIp, userSessionDO.getUserIp());
assertEquals(userAgent, userSessionDO.getUserAgent());
// 校验 LoginUser 缓存
LoginUser redisLoginUser = loginUserRedisDAO.get(sessionId);
assertPojoEquals(loginUser, redisLoginUser, "username", "password");
}
@Test
public void testCreateRefreshUserSession_success() {
// 准备参数
String sessionId = randomString();
String userIp = randomString();
String userAgent = randomString();
long timeLong = randomLongId();
String userName = randomString();
Date date = randomDate();
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue()));
// mock 方法
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
// mock 数据
loginUser.setUpdateTime(date);
loginUserRedisDAO.set(sessionId, loginUser);
UserSessionDO userSession = UserSessionDO.builder().id(sessionId)
.userId(loginUser.getId()).userType(loginUser.getUserType())
.userIp(userIp).userAgent(userAgent).username(userName)
.sessionTimeout(addTime(Duration.ofMillis(timeLong)))
.build();
userSessionMapper.insert(userSession);
// 调用
userSessionService.refreshUserSession(sessionId, loginUser);
// 校验 LoginUser 缓存
LoginUser redisLoginUser = loginUserRedisDAO.get(sessionId);
assertNotEquals(redisLoginUser.getUpdateTime(), date);
// 校验 UserSessionDO 记录
UserSessionDO updateDO = userSessionMapper.selectById(sessionId);
assertEquals(updateDO.getUsername(), loginUser.getUsername());
assertNotEquals(updateDO.getUpdateTime(), userSession.getUpdateTime());
assertNotEquals(updateDO.getSessionTimeout(), addTime(Duration.ofMillis(timeLong)));
}
@Test
public void testDeleteUserSession_success() {
// 准备参数
String sessionId = randomString();
String userIp = randomString();
String userAgent = randomString();
Long timeLong = randomLongId();
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue()));
// mock 存入 Redis
when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
// mock 数据
loginUserRedisDAO.set(sessionId, loginUser);
UserSessionDO userSession = UserSessionDO.builder().id(sessionId)
.userId(loginUser.getId()).userType(loginUser.getUserType())
.userIp(userIp).userAgent(userAgent).username(loginUser.getUsername())
.sessionTimeout(addTime(Duration.ofMillis(timeLong)))
.build();
userSessionMapper.insert(userSession);
// 调用
userSessionService.deleteUserSession(sessionId);
// 校验数据不存在了
assertNull(loginUserRedisDAO.get(sessionId));
assertNull(userSessionMapper.selectById(sessionId));
}
}

View File

@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.system.service.common;
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
import cn.iocoder.yudao.module.system.dal.redis.common.CaptchaRedisDAO;
import cn.iocoder.yudao.module.system.framework.captcha.config.CaptchaProperties;
import cn.iocoder.yudao.framework.test.core.ut.BaseRedisUnitTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static org.junit.jupiter.api.Assertions.*;
@Import({CaptchaServiceImpl.class, CaptchaProperties.class, CaptchaRedisDAO.class})
public class CaptchaServiceTest extends BaseRedisUnitTest {
@Resource
private CaptchaServiceImpl captchaService;
@Resource
private CaptchaRedisDAO captchaRedisDAO;
@Resource
private CaptchaProperties captchaProperties;
@Test
public void testGetCaptchaImage() {
// 调用
CaptchaImageRespVO respVO = captchaService.getCaptchaImage();
// 断言
assertNotNull(respVO.getUuid());
assertNotNull(respVO.getImg());
String captchaCode = captchaRedisDAO.get(respVO.getUuid());
assertNotNull(captchaCode);
}
@Test
public void testGetCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
String resultCode = captchaService.getCaptchaCode(uuid);
// 断言
assertEquals(code, resultCode);
}
@Test
public void testDeleteCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
captchaService.deleteCaptchaCode(uuid);
// 断言
assertNull(captchaRedisDAO.get(uuid));
}
}

View File

@ -0,0 +1,272 @@
package cn.iocoder.yudao.module.system.service.dept;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum;
import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import com.google.common.collect.Multimap;
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 java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
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.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* {@link DeptServiceImpl} 的单元测试类
*
* @author niudehua
*/
@Import(DeptServiceImpl.class)
public class DeptServiceTest extends BaseDbUnitTest {
@Resource
private DeptServiceImpl deptService;
@Resource
private DeptMapper deptMapper;
@MockBean
private DeptProducer deptProducer;
@Test
@SuppressWarnings("unchecked")
void testInitLocalCache() {
// mock 数据
DeptDO deptDO1 = randomDeptDO();
deptMapper.insert(deptDO1);
DeptDO deptDO2 = randomDeptDO();
deptMapper.insert(deptDO2);
// 调用
deptService.initLocalCache();
// 断言 deptCache 缓存
Map<Long, DeptDO> deptCache = (Map<Long, DeptDO>) getFieldValue(deptService, "deptCache");
assertEquals(2, deptCache.size());
assertPojoEquals(deptDO1, deptCache.get(deptDO1.getId()));
assertPojoEquals(deptDO2, deptCache.get(deptDO2.getId()));
// 断言 parentDeptCache 缓存
Multimap<Long, DeptDO> parentDeptCache = (Multimap<Long, DeptDO>) getFieldValue(deptService, "parentDeptCache");
assertEquals(2, parentDeptCache.size());
assertPojoEquals(deptDO1, parentDeptCache.get(deptDO1.getParentId()));
assertPojoEquals(deptDO2, parentDeptCache.get(deptDO2.getParentId()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(deptService, "maxUpdateTime");
assertEquals(ObjectUtils.max(deptDO1.getUpdateTime(), deptDO2.getUpdateTime()), maxUpdateTime);
}
@Test
void testListDepts() {
// mock 数据
DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到
o.setName("开发部");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
deptMapper.insert(dept);
// 测试 name 不匹配
deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setName("")));
// 测试 status 不匹配
deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
DeptListReqVO reqVO = new DeptListReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<DeptDO> sysDeptDOS = deptService.getSimpleDepts(reqVO);
// 断言
assertEquals(1, sysDeptDOS.size());
assertPojoEquals(dept, sysDeptDOS.get(0));
}
@Test
void testCreateDept_success() {
// 准备参数
DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class,
o -> {
o.setParentId(DeptIdEnum.ROOT.getId());
o.setStatus(randomCommonStatus());
});
// 调用
Long deptId = deptService.createDept(reqVO);
// 断言
assertNotNull(deptId);
// 校验记录的属性是否正确
DeptDO deptDO = deptMapper.selectById(deptId);
assertPojoEquals(reqVO, deptDO);
// 校验调用
verify(deptProducer, times(1)).sendDeptRefreshMessage();
}
@Test
void testUpdateDept_success() {
// mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class, o -> {
// 设置更新的 ID
o.setParentId(DeptIdEnum.ROOT.getId());
o.setId(dbDeptDO.getId());
o.setStatus(randomCommonStatus());
});
// 调用
deptService.updateDept(reqVO);
// 校验是否更新正确
DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, deptDO);
}
@Test
void testDeleteDept_success() {
// mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDeptDO.getId();
// 调用
deptService.deleteDept(id);
// 校验数据不存在了
assertNull(deptMapper.selectById(id));
}
@Test
void testCheckDept_nameDuplicateForUpdate() {
// mock 数据
DeptDO deptDO = randomDeptDO();
// 设置根节点部门
deptDO.setParentId(DeptIdEnum.ROOT.getId());
deptMapper.insert(deptDO);
// mock 数据 稍后模拟重复它的 name
DeptDO nameDeptDO = randomDeptDO();
// 设置根节点部门
nameDeptDO.setParentId(DeptIdEnum.ROOT.getId());
deptMapper.insert(nameDeptDO);
// 准备参数
DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class,
o -> {
// 设置根节点部门
o.setParentId(DeptIdEnum.ROOT.getId());
// 设置更新的 ID
o.setId(deptDO.getId());
// 模拟 name 重复
o.setName(nameDeptDO.getName());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.updateDept(reqVO), DEPT_NAME_DUPLICATE);
}
@Test
void testCheckDept_parentNotExitsForCreate() {
DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class,
o -> o.setStatus(randomCommonStatus()));
// 调用,并断言异常
assertServiceException(() -> deptService.createDept(reqVO), DEPT_PARENT_NOT_EXITS);
}
@Test
void testCheckDept_notFoundForDelete() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> deptService.deleteDept(id), DEPT_NOT_FOUND);
}
@Test
void testCheckDept_exitsChildrenForDelete() {
// mock 数据
DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据
// 准备参数
DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> {
o.setParentId(parentDept.getId());
o.setStatus(randomCommonStatus());
});
// 插入子部门
deptMapper.insert(childrenDeptDO);
// 调用, 并断言异常
assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN);
}
@Test
void testCheckDept_parentErrorForUpdate() {
// mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);
// 准备参数
DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class,
o -> {
// 设置自己为父部门
o.setParentId(dbDeptDO.getId());
// 设置更新的 ID
o.setId(dbDeptDO.getId());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_ERROR);
}
@Test
void testCheckDept_notEnableForCreate() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
deptMapper.insert(deptDO);
// 准备参数
DeptCreateReqVO reqVO = randomPojo(DeptCreateReqVO.class,
o -> {
// 设置未启用的部门为副部门
o.setParentId(deptDO.getId());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.createDept(reqVO), DEPT_NOT_ENABLE);
}
@Test
void testCheckDept_parentIsChildForUpdate() {
// mock 数据
DeptDO parentDept = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
deptMapper.insert(parentDept);
DeptDO childDept = randomPojo(DeptDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setParentId(parentDept.getId());
});
deptMapper.insert(childDept);
// 初始化本地缓存
deptService.initLocalCache();
// 准备参数
DeptUpdateReqVO reqVO = randomPojo(DeptUpdateReqVO.class,
o -> {
// 设置自己的子部门为父部门
o.setParentId(childDept.getId());
// 设置更新的 ID
o.setId(parentDept.getId());
});
// 调用, 并断言异常
assertServiceException(() -> deptService.updateDept(reqVO), DEPT_PARENT_IS_CHILD);
}
@SafeVarargs
private static DeptDO randomDeptDO(Consumer<DeptDO>... consumers) {
Consumer<DeptDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(DeptDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,194 @@
package cn.iocoder.yudao.module.system.service.dept;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;
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.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
@Import(PostServiceImpl.class)
public class PostServiceTest extends BaseDbUnitTest {
@Resource
private PostServiceImpl postService;
@Resource
private PostMapper postMapper;
@Test
void testPagePosts() {
// mock 数据
PostDO postDO = randomPojo(PostDO.class, o -> {
o.setName("码仔");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
postMapper.insert(postDO);
// 测试 name 不匹配
postMapper.insert(ObjectUtils.cloneIgnoreId(postDO, o -> o.setName("程序员")));
// 测试 status 不匹配
postMapper.insert(ObjectUtils.cloneIgnoreId(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
PostPageReqVO reqVO = new PostPageReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<PostDO> pageResult = postService.getPostPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(postDO, pageResult.getList().get(0));
}
@Test
void testListPosts() {
// mock 数据
PostDO postDO = randomPojo(PostDO.class, o -> {
o.setName("码仔");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
postMapper.insert(postDO);
// 测试 name 不匹配
postMapper.insert(ObjectUtils.cloneIgnoreId(postDO, o -> o.setName("程序员")));
// 测试 status 不匹配
postMapper.insert(ObjectUtils.cloneIgnoreId(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
PostExportReqVO reqVO = new PostExportReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<PostDO> list = postService.getPosts(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(postDO, list.get(0));
}
@Test
void testGetPost() {
// mock 数据
PostDO dbPostDO = randomPostDO();
postMapper.insert(dbPostDO);
// 准备参数
Long id = dbPostDO.getId();
// 调用
PostDO post = postService.getPost(id);
// 断言
assertNotNull(post);
assertPojoEquals(dbPostDO, post);
}
@Test
void testCreatePost_success() {
// 准备参数
PostCreateReqVO reqVO = randomPojo(PostCreateReqVO.class,
o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()));
// 调用
Long postId = postService.createPost(reqVO);
// 断言
assertNotNull(postId);
// 校验记录的属性是否正确
PostDO post = postMapper.selectById(postId);
assertPojoEquals(reqVO, post);
}
@Test
void testUpdatePost_success() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据
// 准备参数
PostUpdateReqVO reqVO = randomPojo(PostUpdateReqVO.class,
o -> {
// 设置更新的 ID
o.setId(postDO.getId());
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus());
});
// 调用
postService.updatePost(reqVO);
// 校验是否更新正确
PostDO post = postMapper.selectById(reqVO.getId());// 获取最新的
assertPojoEquals(reqVO, post);
}
@Test
void testDeletePost_success() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);
// 准备参数
Long id = postDO.getId();
// 调用
postService.deletePost(id);
assertNull(postMapper.selectById(id));
}
@Test
void testCheckPost_notFoundForDelete() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> postService.deletePost(id), POST_NOT_FOUND);
}
@Test
void testCheckPost_nameDuplicateForCreate() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据
// 准备参数
PostCreateReqVO reqVO = randomPojo(PostCreateReqVO.class,
// 模拟 name 重复
o -> o.setName(postDO.getName()));
assertServiceException(() -> postService.createPost(reqVO), POST_NAME_DUPLICATE);
}
@Test
void testCheckPost_codeDuplicateForUpdate() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);
// mock 数据 稍后模拟重复它的 code
PostDO codePostDO = randomPostDO();
postMapper.insert(codePostDO);
// 准备参数
PostUpdateReqVO reqVO = randomPojo(PostUpdateReqVO.class,
o -> {
// 设置更新的 ID
o.setId(postDO.getId());
// 模拟 code 重复
o.setCode(codePostDO.getCode());
});
// 调用, 并断言异常
assertServiceException(() -> postService.updatePost(reqVO), POST_CODE_DUPLICATE);
}
@SafeVarargs
private static PostDO randomPostDO(Consumer<PostDO>... consumers) {
Consumer<PostDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(PostDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,296 @@
package cn.iocoder.yudao.module.system.service.dict;
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO;
import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper;
import cn.iocoder.yudao.module.system.mq.producer.dict.DictDataProducer;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import com.google.common.collect.ImmutableTable;
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 java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
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.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import(DictDataServiceImpl.class)
public class DictDataServiceTest extends BaseDbUnitTest {
@Resource
private DictDataServiceImpl dictDataService;
@Resource
private DictDataMapper dictDataMapper;
@MockBean
private DictTypeService dictTypeService;
@MockBean
private DictDataProducer dictDataProducer;
/**
* 测试加载到新的字典数据的情况
*/
@Test
@SuppressWarnings("unchecked")
public void testInitLocalCache() {
// mock 数据
DictDataDO dictData01 = randomDictDataDO();
dictDataMapper.insert(dictData01);
DictDataDO dictData02 = randomDictDataDO();
dictDataMapper.insert(dictData02);
// 调用
dictDataService.initLocalCache();
// 断言 labelDictDataCache 缓存
ImmutableTable<String, String, DictDataDO> labelDictDataCache =
(ImmutableTable<String, String, DictDataDO>) getFieldValue(dictDataService, "labelDictDataCache");
assertEquals(2, labelDictDataCache.size());
assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel()));
assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel()));
// 断言 valueDictDataCache 缓存
ImmutableTable<String, String, DictDataDO> valueDictDataCache =
(ImmutableTable<String, String, DictDataDO>) getFieldValue(dictDataService, "valueDictDataCache");
assertEquals(2, valueDictDataCache.size());
assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue()));
assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime");
assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime);
}
@Test
public void testGetDictDataPage() {
// mock 数据
DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到
o.setLabel("芋艿");
o.setDictType("yunai");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
dictDataMapper.insert(dbDictData);
// 测试 label 不匹配
dictDataMapper.insert(ObjectUtils.cloneIgnoreId(dbDictData, o -> o.setLabel("")));
// 测试 dictType 不匹配
dictDataMapper.insert(ObjectUtils.cloneIgnoreId(dbDictData, o -> o.setDictType("nai")));
// 测试 status 不匹配
dictDataMapper.insert(ObjectUtils.cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
DictDataPageReqVO reqVO = new DictDataPageReqVO();
reqVO.setLabel("");
reqVO.setDictType("yu");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictData, pageResult.getList().get(0));
}
@Test
public void testGetDictDataList() {
// mock 数据
DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到
o.setLabel("芋艿");
o.setDictType("yunai");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
dictDataMapper.insert(dbDictData);
// 测试 label 不匹配
dictDataMapper.insert(ObjectUtils.cloneIgnoreId(dbDictData, o -> o.setLabel("")));
// 测试 dictType 不匹配
dictDataMapper.insert(ObjectUtils.cloneIgnoreId(dbDictData, o -> o.setDictType("nai")));
// 测试 status 不匹配
dictDataMapper.insert(ObjectUtils.cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
DictDataExportReqVO reqVO = new DictDataExportReqVO();
reqVO.setLabel("");
reqVO.setDictType("yu");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<DictDataDO> list = dictDataService.getDictDatas(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbDictData, list.get(0));
}
@Test
public void testCreateDictData_success() {
// 准备参数
DictDataCreateReqVO reqVO = randomPojo(DictDataCreateReqVO.class,
o -> o.setStatus(randomCommonStatus()));
// mock 方法
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
Long dictDataId = dictDataService.createDictData(reqVO);
// 断言
assertNotNull(dictDataId);
// 校验记录的属性是否正确
DictDataDO dictData = dictDataMapper.selectById(dictDataId);
assertPojoEquals(reqVO, dictData);
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testUpdateDictData_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
DictDataUpdateReqVO reqVO = randomPojo(DictDataUpdateReqVO.class, o -> {
o.setId(dbDictData.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// mock 方法,字典类型
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
dictDataService.updateDictData(reqVO);
// 校验是否更新正确
DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictData);
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testDeleteDictData_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictData.getId();
// 调用
dictDataService.deleteDictData(id);
// 校验数据不存在了
assertNull(dictDataMapper.selectById(id));
// 校验调用
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
}
@Test
public void testCheckDictDataExists_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 调用成功
dictDataService.checkDictDataExists(dbDictData.getId());
}
@Test
public void testCheckDictDataExists_notExists() {
assertServiceException(() -> dictDataService.checkDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS);
}
@Test
public void testCheckDictTypeValid_success() {
// mock 方法,数据类型被禁用
String type = randomString();
when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type));
// 调用, 成功
dictDataService.checkDictTypeValid(type);
}
@Test
public void testCheckDictTypeValid_notExists() {
assertServiceException(() -> dictDataService.checkDictTypeValid(randomString()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testCheckDictTypeValid_notEnable() {
// mock 方法,数据类型被禁用
String dictType = randomString();
when(dictTypeService.getDictType(eq(dictType))).thenReturn(
randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 调用, 并断言异常
assertServiceException(() -> dictDataService.checkDictTypeValid(dictType), DICT_TYPE_NOT_ENABLE);
}
@Test
public void testCheckDictDataValueUnique_success() {
// 调用,成功
dictDataService.checkDictDataValueUnique(randomLongId(), randomString(), randomString());
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForCreate() {
// 准备参数
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.checkDictDataValueUnique(null, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.checkDictDataValueUnique(id, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
// ========== 随机对象 ==========
@SafeVarargs
private static DictDataDO randomDictDataDO(Consumer<DictDataDO>... consumers) {
Consumer<DictDataDO> consumer = (o) -> {
o.setStatus(randomCommonStatus()); // 保证 status 的范围
};
return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers));
}
/**
* 生成一个有效的字典类型
*
* @param type 字典类型
* @return DictTypeDO 对象
*/
private static DictTypeDO randomDictTypeDO(String type) {
return randomPojo(DictTypeDO.class, o -> {
o.setType(type);
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启
});
}
}

View File

@ -0,0 +1,273 @@
package cn.iocoder.yudao.module.system.service.dict;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeUpdateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO;
import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
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.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@Import(DictTypeServiceImpl.class)
public class DictTypeServiceTest extends BaseDbUnitTest {
@Resource
private DictTypeServiceImpl dictTypeService;
@Resource
private DictTypeMapper dictTypeMapper;
@MockBean
private DictDataService dictDataService;
@Test
public void testGetDictTypePage() {
// mock 数据
DictTypeDO dbDictType = randomPojo(DictTypeDO.class, o -> { // 等会查询到
o.setName("yunai");
o.setType("芋艿");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2021, 1, 15));
});
dictTypeMapper.insert(dbDictType);
// 测试 name 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setName("tudou")));
// 测试 type 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setType("土豆")));
// 测试 status 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
DictTypePageReqVO reqVO = new DictTypePageReqVO();
reqVO.setName("nai");
reqVO.setType("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2021, 1, 10));
reqVO.setEndCreateTime(buildTime(2021, 1, 20));
// 调用
PageResult<DictTypeDO> pageResult = dictTypeService.getDictTypePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictType, pageResult.getList().get(0));
}
@Test
public void testGetDictTypeList() {
// mock 数据
DictTypeDO dbDictType = randomPojo(DictTypeDO.class, o -> { // 等会查询到
o.setName("yunai");
o.setType("芋艿");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2021, 1, 15));
});
dictTypeMapper.insert(dbDictType);
// 测试 name 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setName("tudou")));
// 测试 type 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setType("土豆")));
// 测试 status 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
dictTypeMapper.insert(ObjectUtils.cloneIgnoreId(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
DictTypeExportReqVO reqVO = new DictTypeExportReqVO();
reqVO.setName("nai");
reqVO.setType("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2021, 1, 10));
reqVO.setEndCreateTime(buildTime(2021, 1, 20));
// 调用
List<DictTypeDO> list = dictTypeService.getDictTypeList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbDictType, list.get(0));
}
@Test
public void testGetDictType() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);
// 准备参数
String type = dbDictType.getType();
// 调用
DictTypeDO dictType = dictTypeService.getDictType(type);
// 断言
assertNotNull(dictType);
assertPojoEquals(dbDictType, dictType);
}
@Test
public void testCreateDictType_success() {
// 准备参数
DictTypeCreateReqVO reqVO = randomPojo(DictTypeCreateReqVO.class,
o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()));
// 调用
Long dictTypeId = dictTypeService.createDictType(reqVO);
// 断言
assertNotNull(dictTypeId);
// 校验记录的属性是否正确
DictTypeDO dictType = dictTypeMapper.selectById(dictTypeId);
assertPojoEquals(reqVO, dictType);
}
@Test
public void testUpdateDictType_success() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
DictTypeUpdateReqVO reqVO = randomPojo(DictTypeUpdateReqVO.class, o -> {
o.setId(dbDictType.getId()); // 设置更新的 ID
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus());
});
// 调用
dictTypeService.updateDictType(reqVO);
// 校验是否更新正确
DictTypeDO dictType = dictTypeMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictType);
}
@Test
public void testDeleteDictType_success() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictType.getId();
// 调用
dictTypeService.deleteDictType(id);
// 校验数据不存在了
assertNull(dictTypeMapper.selectById(id));
}
@Test
public void testDeleteDictType_hasChildren() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictType.getId();
// mock 方法
when(dictDataService.countByDictType(eq(dbDictType.getType()))).thenReturn(1L);
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN);
}
@Test
public void testCheckDictDataExists_success() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 调用成功
dictTypeService.checkDictTypeExists(dbDictType.getId());
}
@Test
public void testCheckDictDataExists_notExists() {
assertServiceException(() -> dictTypeService.checkDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testCheckDictTypeUnique_success() {
// 调用,成功
dictTypeService.checkDictTypeUnique(randomLongId(), randomString());
}
@Test
public void testCheckDictTypeUnique_valueDuplicateForCreate() {
// 准备参数
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeUnique(null, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testCheckDictTypeUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeUnique(id, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testCheckDictTypNameUnique_success() {
// 调用,成功
dictTypeService.checkDictTypeNameUnique(randomLongId(), randomString());
}
@Test
public void testCheckDictTypeNameUnique_nameDuplicateForCreate() {
// 准备参数
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(null, name),
DICT_TYPE_NAME_DUPLICATE);
}
@Test
public void testCheckDictTypeNameUnique_nameDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(id, name),
DICT_TYPE_NAME_DUPLICATE);
}
// ========== 随机对象 ==========
@SafeVarargs
private static DictTypeDO randomDictTypeDO(Consumer<DictTypeDO>... consumers) {
Consumer<DictTypeDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(DictTypeDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,293 @@
package cn.iocoder.yudao.module.system.service.errorcode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO;
import cn.iocoder.yudao.module.system.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.mysql.errorcode.ErrorCodeMapper;
import cn.iocoder.yudao.module.system.enums.errorcode.ErrorCodeTypeEnum;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_DUPLICATE;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ERROR_CODE_NOT_EXISTS;
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.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
@Import(ErrorCodeServiceImpl.class)
public class ErrorCodeServiceTest extends BaseDbUnitTest {
@Resource
private ErrorCodeServiceImpl errorCodeService;
@Resource
private ErrorCodeMapper errorCodeMapper;
@Test
public void testCreateErrorCode_success() {
// 准备参数
ErrorCodeCreateReqVO reqVO = randomPojo(ErrorCodeCreateReqVO.class);
// 调用
Long errorCodeId = errorCodeService.createErrorCode(reqVO);
// 断言
assertNotNull(errorCodeId);
// 校验记录的属性是否正确
ErrorCodeDO errorCode = errorCodeMapper.selectById(errorCodeId);
assertPojoEquals(reqVO, errorCode);
assertEquals(ErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
}
@Test
public void testUpdateErrorCode_success() {
// mock 数据
ErrorCodeDO dbErrorCode = randomInfErrorCodeDO();
errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
// 准备参数
ErrorCodeUpdateReqVO reqVO = randomPojo(ErrorCodeUpdateReqVO.class, o -> {
o.setId(dbErrorCode.getId()); // 设置更新的 ID
});
// 调用
errorCodeService.updateErrorCode(reqVO);
// 校验是否更新正确
ErrorCodeDO errorCode = errorCodeMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, errorCode);
assertEquals(ErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
}
@Test
public void testDeleteErrorCode_success() {
// mock 数据
ErrorCodeDO dbErrorCode = randomInfErrorCodeDO();
errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbErrorCode.getId();
// 调用
errorCodeService.deleteErrorCode(id);
// 校验数据不存在了
assertNull(errorCodeMapper.selectById(id));
}
@Test
public void testGetErrorCodePage() {
// mock 数据
ErrorCodeDO dbErrorCode = initGetErrorCodePage();
// 准备参数
ErrorCodePageReqVO reqVO = new ErrorCodePageReqVO();
reqVO.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType());
reqVO.setApplicationName("tu");
reqVO.setCode(1);
reqVO.setMessage("ma");
reqVO.setBeginCreateTime(buildTime(2020, 11, 1));
reqVO.setEndCreateTime(buildTime(2020, 11, 30));
// 调用
PageResult<ErrorCodeDO> pageResult = errorCodeService.getErrorCodePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbErrorCode, pageResult.getList().get(0));
}
/**
* 初始化 getErrorCodePage 方法的测试数据
*/
private ErrorCodeDO initGetErrorCodePage() {
ErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> { // 等会查询到
o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType());
o.setApplicationName("tudou");
o.setCode(1);
o.setMessage("yuanma");
o.setCreateTime(buildTime(2020, 11, 11));
});
errorCodeMapper.insert(dbErrorCode);
// 测试 type 不匹配
errorCodeMapper.insert(ObjectUtils.cloneIgnoreId(dbErrorCode, o -> o.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType())));
// 测试 applicationName 不匹配
errorCodeMapper.insert(ObjectUtils.cloneIgnoreId(dbErrorCode, o -> o.setApplicationName("yuan")));
// 测试 code 不匹配
errorCodeMapper.insert(ObjectUtils.cloneIgnoreId(dbErrorCode, o -> o.setCode(2)));
// 测试 message 不匹配
errorCodeMapper.insert(ObjectUtils.cloneIgnoreId(dbErrorCode, o -> o.setMessage("nai")));
// 测试 createTime 不匹配
errorCodeMapper.insert(ObjectUtils.cloneIgnoreId(dbErrorCode, o -> o.setCreateTime(buildTime(2020, 12, 12))));
return dbErrorCode;
}
@Test
public void testGetErrorCodeList() {
// mock 数据
ErrorCodeDO dbErrorCode = initGetErrorCodePage();
// 准备参数
ErrorCodeExportReqVO reqVO = new ErrorCodeExportReqVO();
reqVO.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType());
reqVO.setApplicationName("tu");
reqVO.setCode(1);
reqVO.setMessage("ma");
reqVO.setBeginCreateTime(buildTime(2020, 11, 1));
reqVO.setEndCreateTime(buildTime(2020, 11, 30));
// 调用
List<ErrorCodeDO> list = errorCodeService.getErrorCodeList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbErrorCode, list.get(0));
}
@Test
public void testValidateCodeDuplicate_codeDuplicateForCreate() {
// 准备参数
Integer code = randomInteger();
// mock 数据
errorCodeMapper.insert(randomInfErrorCodeDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, null),
ERROR_CODE_DUPLICATE);
}
@Test
public void testValidateCodeDuplicate_codeDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
Integer code = randomInteger();
// mock 数据
errorCodeMapper.insert(randomInfErrorCodeDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, id),
ERROR_CODE_DUPLICATE);
}
@Test
public void testValidateErrorCodeExists_notExists() {
assertServiceException(() -> errorCodeService.validateErrorCodeExists(null),
ERROR_CODE_NOT_EXISTS);
}
/**
* 情况 1错误码不存在的情况
*/
@Test
public void testAutoGenerateErrorCodes_01() {
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class);
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言
ErrorCodeDO errorCode = errorCodeMapper.selectOne(null);
assertPojoEquals(generateReqDTO, errorCode);
assertEquals(ErrorCodeTypeEnum.AUTO_GENERATION.getType(), errorCode.getType());
}
/**
* 情况 2.1,错误码存在,但是是 ErrorCodeTypeEnum.MANUAL_OPERATION 类型
*/
@Test
public void testAutoGenerateErrorCodes_021() {
// mock 数据
ErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.MANUAL_OPERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,相等,说明不会更新
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.2,错误码存在,但是是 applicationName 不匹配
*/
@Test
public void testAutoGenerateErrorCodes_022() {
// mock 数据
ErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(randomString()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,相等,说明不会更新
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.3,错误码存在,但是是 message 相同
*/
@Test
public void testAutoGenerateErrorCodes_023() {
// mock 数据
ErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName())
.setMessage(dbErrorCode.getMessage()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,相等,说明不会更新
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(dbErrorCode, errorCode);
}
/**
* 情况 2.3,错误码存在,但是是 message 不同,则进行更新
*/
@Test
public void testAutoGenerateErrorCodes_024() {
// mock 数据
ErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(ErrorCodeTypeEnum.AUTO_GENERATION.getType()));
errorCodeMapper.insert(dbErrorCode);
// 准备参数
ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName()));
// mock 方法
// 调用
errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
// 断言,匹配
ErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
assertPojoEquals(generateReqDTO, errorCode);
}
// ========== 随机对象 ==========
@SafeVarargs
private static ErrorCodeDO randomInfErrorCodeDO(Consumer<ErrorCodeDO>... consumers) {
Consumer<ErrorCodeDO> consumer = (o) -> {
o.setType(randomEle(ErrorCodeTypeEnum.values()).getType()); // 保证 key 的范围
};
return randomPojo(ErrorCodeDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,143 @@
package cn.iocoder.yudao.module.system.service.logger;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.mysql.logger.LoginLogMapper;
import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
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.common.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Import(LoginLogServiceImpl.class)
public class LoginLogServiceImplTest extends BaseDbUnitTest {
@Resource
private LoginLogServiceImpl loginLogService;
@Resource
private LoginLogMapper loginLogMapper;
@Test
public void testGetLoginLogPage() {
// 构造测试数据
// 登录成功的
LoginLogDO loginLogDO = RandomUtils.randomPojo(LoginLogDO.class, logDO -> {
logDO.setLogType(RandomUtil.randomEle(cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum.values()).getType());
logDO.setTraceId(TracerUtils.getTraceId());
logDO.setUserType(RandomUtil.randomEle(UserTypeEnum.values()).getValue());
logDO.setUserIp("192.168.199.16");
logDO.setUsername("wangkai");
logDO.setCreateTime(buildTime(2021, 3, 6));
logDO.setResult(cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.SUCCESS.getResult());
});
loginLogMapper.insert(loginLogDO);
// 下面几个都是不匹配的数据
// 登录失败的
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setResult(cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())));
// 不同ip段的
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setUserIp("192.168.128.18")));
// 不同username
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setUsername("yunai")));
// 构造一个早期时间 2021-02-06 00:00:00
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setCreateTime(buildTime(2021, 2, 6))));
// 构造调用参数
LoginLogPageReqVO reqVO = new LoginLogPageReqVO();
reqVO.setUsername("wangkai");
reqVO.setUserIp("192.168.199");
reqVO.setStatus(true);
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
// 调用service方法
PageResult<LoginLogDO> pageResult = loginLogService.getLoginLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(loginLogDO, pageResult.getList().get(0));
}
@Test
public void testGetLoginLogList() {
// 构造测试数据
// 登录成功的
LoginLogDO loginLogDO = RandomUtils.randomPojo(LoginLogDO.class, logDO -> {
logDO.setLogType(RandomUtil.randomEle(cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum.values()).getType());
logDO.setTraceId(TracerUtils.getTraceId());
logDO.setUserType(RandomUtil.randomEle(UserTypeEnum.values()).getValue());
logDO.setUserIp("192.168.111.16");
logDO.setUsername("wangxiaokai");
logDO.setCreateTime(buildTime(2021, 3, 6));
logDO.setResult(cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.SUCCESS.getResult());
});
loginLogMapper.insert(loginLogDO);
// 下面几个都是不匹配的数据
// 登录失败的
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setResult(cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())));
// 不同ip段的
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setUserIp("192.168.128.18")));
// 不同username
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setUsername("yunai")));
// 构造一个早期时间 2021-02-06 00:00:00
loginLogMapper.insert(ObjectUtils.cloneIgnoreId(loginLogDO, logDO -> logDO.setCreateTime(buildTime(2021, 2, 6))));
// 构造调用参数
LoginLogExportReqVO reqVO = new LoginLogExportReqVO();
reqVO.setUsername("wangxiaokai");
reqVO.setUserIp("192.168.111");
reqVO.setStatus(true);
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
// 调用service方法
List<LoginLogDO> loginLogList = loginLogService.getLoginLogList(reqVO);
// 断言
assertEquals(1, loginLogList.size());
assertPojoEquals(loginLogDO, loginLogList.get(0));
}
@Test
public void testCreateLoginLog() {
LoginLogCreateReqDTO reqDTO = RandomUtils.randomPojo(LoginLogCreateReqDTO.class, vo -> {
// 指定随机的范围,避免超出范围入库失败
vo.setUserType(randomEle(UserTypeEnum.values()).getValue());
vo.setLogType(randomEle(LoginLogTypeEnum.values()).getType());
vo.setResult(randomEle(LoginResultEnum.values()).getResult());
vo.setTraceId(TracerUtils.getTraceId());
});
// 调用
loginLogService.createLoginLog(reqDTO);
// 断言,忽略基本字段
LoginLogDO sysLoginLogDO = loginLogMapper.selectOne(null);
assertPojoEquals(reqDTO, sysLoginLogDO);
}
}

View File

@ -0,0 +1,172 @@
package cn.iocoder.yudao.module.system.service.logger;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.operatelog.core.dto.OperateLogCreateReqDTO;
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
@Import({OperateLogServiceImpl.class})
public class OperateLogServiceImplTest extends BaseDbUnitTest {
@Resource
private OperateLogService operateLogServiceImpl;
@Resource
private OperateLogMapper operateLogMapper;
@MockBean
private AdminUserService userService;
@Test
public void testCreateOperateLogAsync() throws InterruptedException, ExecutionException {
String traceId = TracerUtils.getTraceId();
OperateLogCreateReqDTO reqVO = RandomUtils.randomPojo(OperateLogCreateReqDTO.class, o -> {
o.setTraceId(traceId);
o.setUserId(randomLongId());
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setExts(MapUtil.<String, Object>builder("orderId", randomLongId()).build());
});
// 执行service方法
Future<Boolean> future = operateLogServiceImpl.createOperateLogAsync(reqVO);
future.get();
// 断言插入是否正确
OperateLogDO sysOperateLogDO = operateLogMapper.selectOne("trace_id", traceId);
assertPojoEquals(reqVO, sysOperateLogDO);
}
@Test
public void testGetOperateLogPage() {
// 构造测试数据
// 先构造用户
AdminUserDO user = RandomUtils.randomPojo(AdminUserDO.class, o -> {
o.setNickname("wangkai");
o.setSex(SexEnum.MALE.getSex());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(userService.getUsersByNickname("wangkai")).thenReturn(Collections.singletonList(user));
Long userId = user.getId();
// 构造操作日志
OperateLogDO sysOperateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> {
o.setUserId(userId);
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setModule("order");
o.setType(OperateTypeEnum.CREATE.getType());
o.setStartTime(buildTime(2021, 3, 6));
o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode());
o.setExts(MapUtil.<String, Object>builder("orderId", randomLongId()).build());
});
operateLogMapper.insert(sysOperateLogDO);
// 下面几个是不匹配的数据
// 随机 userId
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setUserId(userId + 1)));
// module 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setModule("user")));
// type 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setType(OperateTypeEnum.IMPORT.getType())));
// createTime 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setStartTime(buildTime(2021, 2, 6))));
// resultCode 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setResultCode(BAD_REQUEST.getCode())));
// 构造调用参数
OperateLogPageReqVO reqVO = new OperateLogPageReqVO();
reqVO.setUserNickname("wangkai");
reqVO.setModule("order");
reqVO.setType(OperateTypeEnum.CREATE.getType());
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
reqVO.setSuccess(true);
// 调用service方法
PageResult<OperateLogDO> pageResult = operateLogServiceImpl.getOperateLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(sysOperateLogDO, pageResult.getList().get(0));
}
@Test
public void testGetOperateLogs() {
// 构造测试数据
// 先构造用户
AdminUserDO user = RandomUtils.randomPojo(AdminUserDO.class, o -> {
o.setNickname("wangkai");
o.setSex(SexEnum.MALE.getSex());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(userService.getUsersByNickname("wangkai")).thenReturn(Collections.singletonList(user));
Long userId = user.getId();
// 构造操作日志
OperateLogDO sysOperateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> {
o.setUserId(userId);
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
o.setModule("order");
o.setType(OperateTypeEnum.CREATE.getType());
o.setStartTime(buildTime(2021, 3, 6));
o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode());
o.setExts(MapUtil.<String, Object>builder("orderId", randomLongId()).build());
});
operateLogMapper.insert(sysOperateLogDO);
// 下面几个是不匹配的数据
// 随机 userId
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setUserId(userId + 1)));
// module 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setModule("user")));
// type 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setType(OperateTypeEnum.IMPORT.getType())));
// createTime 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setStartTime(buildTime(2021, 2, 6))));
// resultCode 不同
operateLogMapper.insert(ObjectUtils.cloneIgnoreId(sysOperateLogDO, logDO -> logDO.setResultCode(BAD_REQUEST.getCode())));
// 构造调用参数
OperateLogExportReqVO reqVO = new OperateLogExportReqVO();
reqVO.setUserNickname("wangkai");
reqVO.setModule("order");
reqVO.setType(OperateTypeEnum.CREATE.getType());
reqVO.setBeginTime(buildTime(2021, 3, 5));
reqVO.setEndTime(buildTime(2021, 3, 7));
reqVO.setSuccess(true);
// 调用 service 方法
List<OperateLogDO> list = operateLogServiceImpl.getOperateLogs(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, list.size());
assertPojoEquals(sysOperateLogDO, list.get(0));
}
}

View File

@ -0,0 +1,163 @@
package cn.iocoder.yudao.module.system.service.notice;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO;
import cn.iocoder.yudao.module.system.dal.mysql.notice.NoticeMapper;
import cn.iocoder.yudao.module.system.enums.notice.NoticeTypeEnum;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND;
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.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
@Import(NoticeServiceImpl.class)
class NoticeServiceImplTest extends BaseDbUnitTest {
@Resource
private NoticeServiceImpl sysNoticeService;
@Resource
private NoticeMapper sysNoticeMapper;
@Test
public void testPageNotices_success() {
// 插入前置数据
NoticeDO dbNotice = randomPojo(NoticeDO.class, o -> {
o.setTitle("尼古拉斯赵四来啦!");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setType(randomEle(NoticeTypeEnum.values()).getType());
});
sysNoticeMapper.insert(dbNotice);
// 测试 title 不匹配
sysNoticeMapper.insert(ObjectUtils.cloneIgnoreId(dbNotice, o -> o.setTitle("尼古拉斯凯奇也来啦!")));
// 测试 status 不匹配
sysNoticeMapper.insert(ObjectUtils.cloneIgnoreId(dbNotice, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 查询
NoticePageReqVO reqVO = new NoticePageReqVO();
reqVO.setTitle("尼古拉斯赵四来啦!");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
PageResult<NoticeDO> pageResult = sysNoticeService.pageNotices(reqVO);
// 验证查询结果经过筛选
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotice, pageResult.getList().get(0));
}
@Test
public void testGetNotice_success() {
// 插入前置数据
NoticeDO dbNotice = randomNoticeDO();
sysNoticeMapper.insert(dbNotice);
// 查询
NoticeDO notice = sysNoticeService.getNotice(dbNotice.getId());
// 验证插入与读取对象是否一致
assertNotNull(notice);
assertPojoEquals(dbNotice, notice);
}
@Test
public void testCreateNotice_success() {
// 准备参数
NoticeCreateReqVO reqVO = randomNoticeCreateReqVO();
// 校验插入是否成功
Long noticeId = sysNoticeService.createNotice(reqVO);
assertNotNull(noticeId);
// 校验插入属性是否正确
NoticeDO notice = sysNoticeMapper.selectById(noticeId);
assertPojoEquals(reqVO, notice);
}
@Test
public void testUpdateNotice_success() {
// 插入前置数据
NoticeDO dbNoticeDO = randomNoticeDO();
sysNoticeMapper.insert(dbNoticeDO);
// 准备更新参数
NoticeUpdateReqVO reqVO = randomNoticeUpdateReqVO(o -> o.setId(dbNoticeDO.getId()));
// 更新
sysNoticeService.updateNotice(reqVO);
// 检验是否更新成功
NoticeDO notice = sysNoticeMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, notice);
}
@Test
public void testDeleteNotice_success() {
// 插入前置数据
NoticeDO dbNotice = randomNoticeDO();
sysNoticeMapper.insert(dbNotice);
// 删除
sysNoticeService.deleteNotice(dbNotice.getId());
// 检查是否删除成功
assertNull(sysNoticeMapper.selectById(dbNotice.getId()));
}
@Test
public void checkNoticeExists_success() {
// 插入前置数据
NoticeDO dbNotice = randomNoticeDO();
sysNoticeMapper.insert(dbNotice);
// 成功调用
sysNoticeService.checkNoticeExists(dbNotice.getId());
}
@Test
public void checkNoticeExists_noExists() {
assertServiceException(() -> sysNoticeService.checkNoticeExists(randomLongId()), NOTICE_NOT_FOUND);
}
@SafeVarargs
private static NoticeDO randomNoticeDO(Consumer<NoticeDO>... consumers) {
NoticeDO notice = randomPojo(NoticeDO.class, consumers);
notice.setType(randomEle(NoticeTypeEnum.values()).getType());
notice.setStatus(CommonStatusEnum.ENABLE.getStatus());
return notice;
}
@SafeVarargs
private static NoticeUpdateReqVO randomNoticeUpdateReqVO(Consumer<NoticeUpdateReqVO>... consumers) {
NoticeUpdateReqVO reqVO = randomPojo(NoticeUpdateReqVO.class, consumers);
reqVO.setType(randomEle(NoticeTypeEnum.values()).getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
return reqVO;
}
private static NoticeCreateReqVO randomNoticeCreateReqVO() {
NoticeCreateReqVO reqVO = randomPojo(NoticeCreateReqVO.class);
reqVO.setType(randomEle(NoticeTypeEnum.values()).getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
return reqVO;
}
}

View File

@ -0,0 +1,393 @@
package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringAopUtils;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper;
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import com.google.common.collect.Multimap;
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 java.util.*;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
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.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
// TODO @芋艿:单测的代码质量可以提升下
@Import(MenuServiceImpl.class)
public class MenuServiceTest extends BaseDbUnitTest {
@Resource
private MenuServiceImpl menuService;
@Resource
private MenuMapper menuMapper;
@MockBean
private PermissionService permissionService;
@MockBean
private MenuProducer menuProducer;
@MockBean
private TenantService tenantService;
@Test
public void testInitLocalCache_success() throws Exception {
MenuDO menuDO1 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L);
menuMapper.insert(menuDO1);
MenuDO menuDO2 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L);
menuMapper.insert(menuDO2);
// 调用
menuService.initLocalCache();
// 获取代理对象
MenuServiceImpl target = (MenuServiceImpl) SpringAopUtils.getTarget(menuService);
Map<Long, MenuDO> menuCache =
(Map<Long, MenuDO>) BeanUtil.getFieldValue(target, "menuCache");
Assert.isTrue(menuCache.size() == 2);
assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId()));
assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId()));
Multimap<String, MenuDO> permissionMenuCache =
(Multimap<String, MenuDO>) BeanUtil.getFieldValue(target, "permissionMenuCache");
Assert.isTrue(permissionMenuCache.size() == 2);
assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission()));
assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission()));
Date maxUpdateTime = (Date) BeanUtil.getFieldValue(target, "maxUpdateTime");
assertEquals(ObjectUtils.max(menuDO1.getUpdateTime(), menuDO2.getUpdateTime()), maxUpdateTime);
}
@Test
public void testCreateMenu_success() {
//构造父目录
MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
//调用
MenuCreateReqVO vo = randomPojo(MenuCreateReqVO.class, o -> {
o.setParentId(parentId);
o.setName("testSonName");
o.setType(MenuTypeEnum.MENU.getType());
o.setStatus(randomCommonStatus());
});
Long menuId = menuService.createMenu(vo);
//断言
assertNotNull(menuId);
// 校验记录的属性是否正确
MenuDO ret = menuMapper.selectById(menuId);
assertPojoEquals(vo, ret);
// 校验调用
verify(menuProducer).sendMenuRefreshMessage();
}
@Test
public void testUpdateMenu_success() {
//构造父子目录
MenuDO sonMenuDO = initParentAndSonMenuDO();
Long sonId = sonMenuDO.getId();
Long parentId = sonMenuDO.getParentId();
//调用
MenuUpdateReqVO vo = randomPojo(MenuUpdateReqVO.class, o -> {
o.setId(sonId);
o.setParentId(parentId);
o.setType(MenuTypeEnum.MENU.getType());
o.setStatus(randomCommonStatus());
o.setName("pppppp"); //修改名字
});
menuService.updateMenu(vo);
//断言
// 校验记录的属性是否正确
MenuDO ret = menuMapper.selectById(sonId);
assertPojoEquals(vo, ret);
// 校验调用
verify(menuProducer).sendMenuRefreshMessage();
}
@Test
public void testUpdateMenu_sonIdNotExist() {
Long sonId = 99999L;
Long parentId = 10000L;
//调用
MenuUpdateReqVO vo = randomPojo(MenuUpdateReqVO.class, o -> {
o.setId(sonId);
o.setParentId(parentId);
o.setType(MenuTypeEnum.MENU.getType());
o.setStatus(randomCommonStatus());
});
//断言
assertServiceException(() -> menuService.updateMenu(vo), MENU_NOT_EXISTS);
}
@Test
public void testDeleteMenu_success() {
MenuDO sonMenuDO = initParentAndSonMenuDO();
Long sonId = sonMenuDO.getId();
// 调用
menuService.deleteMenu(sonId);
// 断言
MenuDO menuDO = menuMapper.selectById(sonId);
assertNull(menuDO);
verify(permissionService).processMenuDeleted(sonId);
verify(menuProducer).sendMenuRefreshMessage();
}
@Test
public void testDeleteMenu_menuNotExist() {
Long sonId = 99999L;
assertServiceException(() -> menuService.deleteMenu(sonId), MENU_NOT_EXISTS);
}
@Test
public void testDeleteMenu_existChildren() {
MenuDO sonMenu = initParentAndSonMenuDO();
Long parentId = sonMenu.getParentId();
assertServiceException(() -> menuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN);
}
@Test
public void testGetMenus() {
// mock 数据
MenuDO menu100 = randomPojo(MenuDO.class, o -> o.setId(100L).setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class, o -> o.setId(101L).setStatus(CommonStatusEnum.DISABLE.getStatus()));
menuMapper.insert(menu101);
// 准备参数
MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<MenuDO> result = menuService.getMenus(reqVO);
// 断言
assertEquals(1, result.size());
assertPojoEquals(menu100, result.get(0));
}
@Test
public void testTenantMenus() {
// mock 数据
MenuDO menu100 = randomPojo(MenuDO.class, o -> o.setId(100L).setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class, o -> o.setId(101L).setStatus(CommonStatusEnum.DISABLE.getStatus()));
menuMapper.insert(menu101);
MenuDO menu102 = randomPojo(MenuDO.class, o -> o.setId(102L).setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuMapper.insert(menu102);
// mock 过滤菜单
// mock 账户额度充足
Set<Long> menuIds = asSet(100L, 101L);
doNothing().when(tenantService).handleTenantMenu(argThat(handler -> {
handler.handle(menuIds);
return true;
}));
// 准备参数
MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<MenuDO> result = menuService.getTenantMenus(reqVO);
// 断言
assertEquals(1, result.size());
assertPojoEquals(menu100, result.get(0));
}
@Test
public void testGetMenusReqVo_success() {
Map<Long, MenuDO> idMenuMap = new HashMap<>();
// 用于验证可以模糊搜索名称包含"name"状态为1的menu
MenuDO menu = createMenuDO(MenuTypeEnum.MENU, "name2", 0L, 1);
menuMapper.insert(menu);
idMenuMap.put(menu.getId(), menu);
menu = createMenuDO(MenuTypeEnum.MENU, "11name111", 0L, 1);
menuMapper.insert(menu);
idMenuMap.put(menu.getId(), menu);
menu = createMenuDO(MenuTypeEnum.MENU, "name", 0L, 1);
menuMapper.insert(menu);
idMenuMap.put(menu.getId(), menu);
// 以下是不符合搜索条件的的menu
menu = createMenuDO(MenuTypeEnum.MENU, "xxxxxx", 0L, 1);
menuMapper.insert(menu);
menu = createMenuDO(MenuTypeEnum.MENU, "name", 0L, 2);
menuMapper.insert(menu);
// 调用
MenuListReqVO reqVO = new MenuListReqVO();
reqVO.setStatus(1);
reqVO.setName("name");
List<MenuDO> menuDOS = menuService.getMenus(reqVO);
// 断言
assertEquals(menuDOS.size(), idMenuMap.size());
menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m));
}
@Test
public void testListMenusFromCache_success() throws Exception {
Map<Long, MenuDO> mockCacheMap = new HashMap<>();
// 获取代理对象
MenuServiceImpl target = (MenuServiceImpl) SpringAopUtils.getTarget(menuService);
BeanUtil.setFieldValue(target, "menuCache", mockCacheMap);
Map<Long, MenuDO> idMenuMap = new HashMap<>();
// 用于验证搜索类型为MENU,状态为1的menu
MenuDO menuDO = createMenuDO(1L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
idMenuMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(2L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
idMenuMap.put(menuDO.getId(), menuDO);
// 以下是不符合搜索条件的menu
menuDO = createMenuDO(3L, MenuTypeEnum.BUTTON, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
mockCacheMap.put(menuDO.getId(), menuDO);
List<MenuDO> menuDOS = menuService.getMenuListFromCache(Collections.singletonList(MenuTypeEnum.MENU.getType()),
Collections.singletonList(CommonStatusEnum.DISABLE.getStatus()));
assertEquals(menuDOS.size(), idMenuMap.size());
menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m));
}
@Test
public void testListMenusFromCache2_success() throws Exception {
Map<Long, MenuDO> mockCacheMap = new HashMap<>();
// 获取代理对象
MenuServiceImpl target = (MenuServiceImpl) SpringAopUtils.getTarget(menuService);
BeanUtil.setFieldValue(target, "menuCache", mockCacheMap);
Map<Long, MenuDO> idMenuMap = new HashMap<>();
// 验证搜索id为1, 类型为MENU, 状态为1 的menu
MenuDO menuDO = createMenuDO(1L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
idMenuMap.put(menuDO.getId(), menuDO);
// 以下是不符合搜索条件的menu
menuDO = createMenuDO(2L, MenuTypeEnum.MENU, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(3L, MenuTypeEnum.BUTTON, "name", 0L, 1);
mockCacheMap.put(menuDO.getId(), menuDO);
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
mockCacheMap.put(menuDO.getId(), menuDO);
List<MenuDO> menuDOS = menuService.getMenuListFromCache(Collections.singletonList(1L),
Collections.singletonList(MenuTypeEnum.MENU.getType()), Collections.singletonList(1));
assertEquals(menuDOS.size(), idMenuMap.size());
menuDOS.forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu));
}
@Test
public void testCheckParentResource_success() {
MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
menuService.checkParentResource(parentId, null);
}
@Test
public void testCheckParentResource_canNotSetSelfToBeParent() {
assertServiceException(() -> menuService.checkParentResource(1L, 1L), MENU_PARENT_ERROR);
}
@Test
public void testCheckParentResource_parentNotExist() {
assertServiceException(() -> menuService.checkParentResource(randomLongId(), null), MENU_PARENT_NOT_EXISTS);
}
@Test
public void testCheckParentResource_parentTypeError() {
MenuDO menuDO = createMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
assertServiceException(() -> menuService.checkParentResource(parentId, null), MENU_PARENT_NOT_DIR_OR_MENU);
}
@Test
public void testCheckResource_success() {
MenuDO sonMenu = initParentAndSonMenuDO();
Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId();
String otherSonMenuName = randomString();
menuService.checkResource(parentId, otherSonMenuName, otherSonMenuId);
}
@Test
public void testCheckResource_sonMenuNameDuplicate(){
MenuDO sonMenu=initParentAndSonMenuDO();
Long parentId=sonMenu.getParentId();
Long otherSonMenuId=randomLongId();
String otherSonMenuName=sonMenu.getName(); //相同名称
assertServiceException(() -> menuService.checkResource(parentId, otherSonMenuName, otherSonMenuId), MENU_NAME_DUPLICATE);
}
/**
* 构造父子目录,返回子目录
*
* @return
*/
private MenuDO initParentAndSonMenuDO() {
//构造父子目录
MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
MenuDO sonMenuDO = createMenuDO(MenuTypeEnum.MENU, "testSonName", parentId);
menuMapper.insert(sonMenuDO);
return sonMenuDO;
}
private MenuDO createMenuDO(MenuTypeEnum typeEnum, String menuName, Long parentId) {
return createMenuDO(typeEnum, menuName, parentId, randomCommonStatus());
}
private MenuDO createMenuDO(MenuTypeEnum typeEnum, String menuName, Long parentId, Integer status) {
return createMenuDO(null, typeEnum, menuName, parentId, status);
}
private MenuDO createMenuDO(Long id, MenuTypeEnum typeEnum, String menuName, Long parentId, Integer status) {
return randomPojo(MenuDO.class, o -> {
o.setId(id);
o.setParentId(parentId);
o.setType(typeEnum.getType());
o.setStatus(status);
o.setName(menuName);
});
}
}

View File

@ -0,0 +1,235 @@
package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Import({PermissionServiceImpl.class,
RoleMenuBatchInsertMapper.class, UserRoleBatchInsertMapper.class})
public class PermissionServiceTest extends BaseDbUnitTest {
@Resource
private PermissionServiceImpl permissionService;
@Resource
private RoleMenuMapper roleMenuMapper;
@Resource
private RoleMenuBatchInsertMapper roleMenuBatchInsertMapper;
@Resource
private UserRoleMapper userRoleMapper;
@Resource
private UserRoleBatchInsertMapper userRoleBatchInsertMapper;
@MockBean
private RoleService roleService;
@MockBean
private MenuService menuService;
@MockBean
private DeptService deptService;
@MockBean
private PermissionProducer permissionProducer;
@Test
public void testProcessRoleDeleted() {
// 准备参数
Long roleId = randomLongId();
// mock 数据 UserRole
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除
userRoleMapper.insert(userRoleDO01);
UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除
userRoleMapper.insert(userRoleDO02);
// mock 数据 RoleMenu
RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除
roleMenuMapper.insert(roleMenuDO01);
RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除
roleMenuMapper.insert(roleMenuDO02);
// 调用
permissionService.processRoleDeleted(roleId);
// 断言数据 RoleMenuDO
List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
assertEquals(1, dbRoleMenus.size());
assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
// 断言数据 UserRoleDO
List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();
assertEquals(1, dbUserRoles.size());
assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
// 断言调用
verify(permissionProducer).sendRoleMenuRefreshMessage();
}
@Test
public void testProcessMenuDeleted() {
// 准备参数
Long menuId = randomLongId();
// mock 数据
RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除
roleMenuMapper.insert(roleMenuDO01);
RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除
roleMenuMapper.insert(roleMenuDO02);
// 调用
permissionService.processMenuDeleted(menuId);
// 断言数据
List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
assertEquals(1, dbRoleMenus.size());
assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
// 断言调用
verify(permissionProducer).sendRoleMenuRefreshMessage();
}
@Test
public void testProcessUserDeleted() {
// 准备参数
Long userId = randomLongId();
// mock 数据
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(userId)); // 被删除
userRoleMapper.insert(userRoleDO01);
UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除
userRoleMapper.insert(userRoleDO02);
// 调用
permissionService.processUserDeleted(userId);
// 断言数据
List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();
assertEquals(1, dbUserRoles.size());
assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
}
@Test // 测试从 context 获取的场景
public void testGetDeptDataPermission_fromContext() {
// 准备参数
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 方法
DeptDataPermissionRespDTO respDTO = new DeptDataPermissionRespDTO();
loginUser.setContext(PermissionServiceImpl.CONTEXT_KEY, respDTO);
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser);
// 断言
assertSame(respDTO, result);
}
@Test
public void testGetDeptDataPermission_All() {
// 准备参数
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 方法
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser);
// 断言
assertTrue(result.getAll());
assertFalse(result.getSelf());
assertTrue(CollUtil.isEmpty(result.getDeptIds()));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
}
@Test
public void testGetDeptDataPermission_DeptCustom() {
// 准备参数
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 方法
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser);
// 断言
assertFalse(result.getAll());
assertFalse(result.getSelf());
assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size());
assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds()));
assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId()));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
}
@Test
public void testGetDeptDataPermission_DeptOnly() {
// 准备参数
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 方法
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser);
// 断言
assertFalse(result.getAll());
assertFalse(result.getSelf());
assertEquals(1, result.getDeptIds().size());
assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId()));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
}
@Test
public void testGetDeptDataPermission_DeptAndChild() {
// 准备参数
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 方法(角色)
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO));
// mock 方法(部门)
DeptDO deptDO = randomPojo(DeptDO.class);
when(deptService.getDeptsByParentIdFromCache(eq(loginUser.getDeptId()), eq(true)))
.thenReturn(singletonList(deptDO));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser);
// 断言
assertFalse(result.getAll());
assertFalse(result.getSelf());
assertEquals(2, result.getDeptIds().size());
assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId()));
assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId()));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
}
@Test
public void testGetDeptDataPermission_Self() {
// 准备参数
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 方法
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser);
// 断言
assertFalse(result.getAll());
assertTrue(result.getSelf());
assertTrue(CollUtil.isEmpty(result.getDeptIds()));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
}
}

View File

@ -0,0 +1,298 @@
package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
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.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
// TODO @芋艿:单测的代码质量可以提升下
@Import(RoleServiceImpl.class)
public class RoleServiceTest extends BaseDbUnitTest {
@Resource
private RoleServiceImpl roleService;
@Resource
private RoleMapper roleMapper;
@MockBean
private PermissionService permissionService;
@MockBean
private RoleProducer roleProducer;
@Test
public void testInitLocalCache() {
RoleDO roleDO1 = randomRole();
roleMapper.insert(roleDO1);
RoleDO roleDO2 = randomRole();
roleMapper.insert(roleDO2);
// 调用
roleService.initLocalCache();
// 断言 roleCache 缓存
Map<Long, RoleDO> roleCache = roleService.getRoleCache();
assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId()));
assertPojoEquals(roleDO2, roleCache.get(roleDO2.getId()));
// 断言 maxUpdateTime 缓存
assertEquals(max(roleDO1.getUpdateTime(), roleDO2.getUpdateTime()), roleService.getMaxUpdateTime());
}
@Test
public void testCreateRole_success() {
// 准备参数
RoleCreateReqVO reqVO = randomPojo(RoleCreateReqVO.class);
// 调用
Long roleId = roleService.createRole(reqVO, null);
// 断言
assertNotNull(roleId);
RoleDO roleDO = roleMapper.selectById(roleId);
assertPojoEquals(reqVO, roleDO);
assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType());
assertEquals(CommonStatusEnum.ENABLE.getStatus(), roleDO.getStatus());
assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope());
// verify 发送刷新消息
verify(roleProducer).sendRoleRefreshMessage();
}
@Test
public void testUpdateRole_success() {
// mock 数据
RoleDO roleDO = createRoleDO("role_name", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
RoleUpdateReqVO reqVO = randomPojo(RoleUpdateReqVO.class, o -> {
o.setId(roleId);
o.setCode("role_code");
o.setName("update_name");
o.setSort(999);
});
roleService.updateRole(reqVO);
//断言
RoleDO newRoleDO = roleMapper.selectById(roleId);
assertPojoEquals(reqVO, newRoleDO);
verify(roleProducer).sendRoleRefreshMessage();
}
@Test
public void testUpdateRoleStatus_success() {
RoleDO roleDO = createRoleDO("role_name", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, CommonStatusEnum.ENABLE.getStatus());
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
roleService.updateRoleStatus(roleId, CommonStatusEnum.DISABLE.getStatus());
//断言
RoleDO newRoleDO = roleMapper.selectById(roleId);
assertEquals(CommonStatusEnum.DISABLE.getStatus(), newRoleDO.getStatus());
verify(roleProducer).sendRoleRefreshMessage();
}
@Test
public void testUpdateRoleDataScope_success() {
RoleDO roleDO = createRoleDO("role_name", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
Set<Long> deptIdSet = Arrays.asList(1L, 2L, 3L, 4L, 5L).stream().collect(Collectors.toSet());
roleService.updateRoleDataScope(roleId, DataScopeEnum.DEPT_CUSTOM.getScope(), deptIdSet);
//断言
RoleDO newRoleDO = roleMapper.selectById(roleId);
assertEquals(DataScopeEnum.DEPT_CUSTOM.getScope(), newRoleDO.getDataScope());
Set<Long> newDeptIdSet = newRoleDO.getDataScopeDeptIds();
assertTrue(deptIdSet.size() == newDeptIdSet.size());
deptIdSet.stream().forEach(d -> assertTrue(newDeptIdSet.contains(d)));
verify(roleProducer).sendRoleRefreshMessage();
}
@Test
public void testDeleteRole_success() {
RoleDO roleDO = createRoleDO("role_name", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
//调用
roleService.deleteRole(roleId);
//断言
RoleDO newRoleDO = roleMapper.selectById(roleId);
assertNull(newRoleDO);
verify(roleProducer).sendRoleRefreshMessage();
}
@Test
public void testGetRoles_success() {
Map<Long, RoleDO> idRoleMap = new HashMap<>();
// 验证查询状态为1的角色
RoleDO roleDO1 = createRoleDO("role1", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1);
roleMapper.insert(roleDO1);
idRoleMap.put(roleDO1.getId(), roleDO1);
RoleDO roleDO2 = createRoleDO("role2", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1);
roleMapper.insert(roleDO2);
idRoleMap.put(roleDO2.getId(), roleDO2);
// 以下是排除的角色
RoleDO roleDO3 = createRoleDO("role3", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2);
roleMapper.insert(roleDO3);
//调用
List<RoleDO> roles = roleService.getRoles(Arrays.asList(1));
//断言
assertEquals(2, roles.size());
roles.stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r));
}
@Test
public void testGetRolePage_success() {
Map<Long, RoleDO> idRoleMap = new HashMap<>();
// 验证名称包含"role", 状态为1,code为"code"的角色
// 第一页
RoleDO roleDO = createRoleDO("role1", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code");
roleMapper.insert(roleDO);
idRoleMap.put(roleDO.getId(), roleDO);
// 第二页
roleDO = createRoleDO("role2", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code");
roleMapper.insert(roleDO);
// 以下是排除的角色
roleDO = createRoleDO("role3", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2, "code");
roleMapper.insert(roleDO);
roleDO = createRoleDO("role4", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "xxxxx");
roleMapper.insert(roleDO);
//调用
RolePageReqVO reqVO = randomPojo(RolePageReqVO.class, o -> {
o.setName("role");
o.setCode("code");
o.setStatus(1);
o.setPageNo(1);
o.setPageSize(1);
o.setBeginTime(null);
o.setEndTime(null);
});
PageResult<RoleDO> result = roleService.getRolePage(reqVO);
assertEquals(2, result.getTotal());
result.getList().stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r));
}
@Test
public void testCheckDuplicateRole_success() {
roleService.checkDuplicateRole(randomString(), randomString(), null);
}
@Test
public void testCheckDuplicateRole_nameDuplicate() {
RoleDO roleDO = createRoleDO("role_name", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
String duplicateName = "role_name";
assertServiceException(() -> roleService.checkDuplicateRole(duplicateName, randomString(), null), ROLE_NAME_DUPLICATE, duplicateName);
}
@Test
public void testCheckDuplicateRole_codeDuplicate() {
RoleDO roleDO = randomPojo(RoleDO.class, o -> {
o.setName("role_999");
o.setCode("code");
o.setType(RoleTypeEnum.CUSTOM.getType());
o.setStatus(1);
o.setDataScope(DataScopeEnum.ALL.getScope());
});
roleMapper.insert(roleDO);
String randomName = randomString();
String duplicateCode = "code";
assertServiceException(() -> roleService.checkDuplicateRole(randomName, duplicateCode, null), ROLE_CODE_DUPLICATE, duplicateCode);
}
@Test
public void testCheckUpdateRole_success() {
RoleDO roleDO = createRoleDO("role_name", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
roleService.checkUpdateRole(roleId);
}
@Test
public void testCheckUpdateRole_roleIdNotExist() {
assertServiceException(() -> roleService.checkUpdateRole(randomLongId()), ROLE_NOT_EXISTS);
}
@Test
public void testCheckUpdateRole_systemRoleCanNotBeUpdate() {
RoleDO roleDO = createRoleDO("role_name", RoleTypeEnum.SYSTEM, DataScopeEnum.ALL);
roleMapper.insert(roleDO);
Long roleId = roleDO.getId();
assertServiceException(() -> roleService.checkUpdateRole(roleId), ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
}
private RoleDO createRoleDO(String name, RoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status) {
return createRoleDO( name, typeEnum, scopeEnum, status, randomString());
}
private RoleDO createRoleDO(String name, RoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status, String code) {
return createRoleDO(null, name, typeEnum, scopeEnum, status, code);
}
private RoleDO createRoleDO(String name, RoleTypeEnum typeEnum, DataScopeEnum scopeEnum) {
return createRoleDO(null, name, typeEnum, scopeEnum, randomCommonStatus(), randomString());
}
private RoleDO createRoleDO(Long id, String name, RoleTypeEnum typeEnum, DataScopeEnum scopeEnum, Integer status, String code) {
return randomPojo(RoleDO.class, o -> {
o.setId(id);
o.setName(name);
o.setType(typeEnum.getType());
o.setStatus(status);
o.setDataScope(scopeEnum.getScope());
o.setCode(code);
});
}
private RoleDO randomRole() {
return randomPojo(RoleDO.class,
o -> o.setDataScope(RandomUtil.randomEle(DataScopeEnum.values()).getScope()));
}
}

View File

@ -0,0 +1,246 @@
package cn.iocoder.yudao.module.system.service.sensitiveword;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
import cn.iocoder.yudao.module.system.dal.mysql.sensitiveword.SensitiveWordMapper;
import cn.iocoder.yudao.module.system.mq.producer.sensitiveword.SensitiveWordProducer;
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 java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
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.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
/**
* {@link SensitiveWordServiceImpl} 的单元测试类
*
* @author 永不言败
*/
@Import(SensitiveWordServiceImpl.class)
public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
@Resource
private SensitiveWordServiceImpl sensitiveWordService;
@Resource
private SensitiveWordMapper sensitiveWordMapper;
@MockBean
private SensitiveWordProducer sensitiveWordProducer;
@Test
public void testInitLocalCache() {
SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜")
.setTags(singletonList("论坛")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
sensitiveWordMapper.insert(wordDO1);
SensitiveWordDO wordDO2 = randomPojo(SensitiveWordDO.class, o -> o.setName("笨蛋")
.setTags(singletonList("蔬菜")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
sensitiveWordMapper.insert(wordDO2);
// 调用
sensitiveWordService.initLocalCache();
// 断言 maxUpdateTime 缓存
assertEquals(max(wordDO1.getUpdateTime(), wordDO2.getUpdateTime()), sensitiveWordService.getMaxUpdateTime());
// 断言 sensitiveWordTagsCache 缓存
assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTags());
// 断言 tagSensitiveWordTries 缓存
assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie());
assertEquals(2, sensitiveWordService.getTagSensitiveWordTries().size());
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("论坛"));
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("蔬菜"));
}
@Test
public void testCreateSensitiveWord_success() {
// 准备参数
SensitiveWordCreateReqVO reqVO = randomPojo(SensitiveWordCreateReqVO.class);
// 调用
Long sensitiveWordId = sensitiveWordService.createSensitiveWord(reqVO);
// 断言
assertNotNull(sensitiveWordId);
// 校验记录的属性是否正确
SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(sensitiveWordId);
assertPojoEquals(reqVO, sensitiveWord);
verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage();
}
@Test
public void testUpdateSensitiveWord_success() {
// mock 数据
SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class);
sensitiveWordMapper.insert(dbSensitiveWord);// @Sql: 先插入出一条存在的数据
// 准备参数
SensitiveWordUpdateReqVO reqVO = randomPojo(SensitiveWordUpdateReqVO.class, o -> {
o.setId(dbSensitiveWord.getId()); // 设置更新的 ID
});
// 调用
sensitiveWordService.updateSensitiveWord(reqVO);
// 校验是否更新正确
SensitiveWordDO sensitiveWord = sensitiveWordMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, sensitiveWord);
verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage();
}
@Test
public void testUpdateSensitiveWord_notExists() {
// 准备参数
SensitiveWordUpdateReqVO reqVO = randomPojo(SensitiveWordUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> sensitiveWordService.updateSensitiveWord(reqVO), SENSITIVE_WORD_NOT_EXISTS);
}
@Test
public void testDeleteSensitiveWord_success() {
// mock 数据
SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class);
sensitiveWordMapper.insert(dbSensitiveWord);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSensitiveWord.getId();
// 调用
sensitiveWordService.deleteSensitiveWord(id);
// 校验数据不存在了
assertNull(sensitiveWordMapper.selectById(id));
verify(sensitiveWordProducer).sendSensitiveWordRefreshMessage();
}
@Test
public void testDeleteSensitiveWord_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> sensitiveWordService.deleteSensitiveWord(id), SENSITIVE_WORD_NOT_EXISTS);
}
@Test
public void testGetSensitiveWordPage() {
// mock 数据
SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class, o -> { // 等会查询到
o.setName("笨蛋");
o.setTags(Arrays.asList("论坛", "蔬菜"));
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(DateUtils.buildTime(2022, 2, 8));
});
sensitiveWordMapper.insert(dbSensitiveWord);
// 测试 name 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setName("傻瓜")));
// 测试 tags 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setTags(Arrays.asList("短信", "日用品"))));
// 测试 createTime 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setCreateTime(DateUtils.buildTime(2022, 2, 16))));
// 准备参数
SensitiveWordPageReqVO reqVO = new SensitiveWordPageReqVO();
reqVO.setName("");
reqVO.setTag("论坛");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(DateUtils.buildTime(2022, 2, 1));
reqVO.setEndCreateTime(DateUtils.buildTime(2022, 2, 12));
// 调用
PageResult<SensitiveWordDO> pageResult = sensitiveWordService.getSensitiveWordPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSensitiveWord, pageResult.getList().get(0));
}
@Test
public void testGetSensitiveWordList() {
// mock 数据
SensitiveWordDO dbSensitiveWord = randomPojo(SensitiveWordDO.class, o -> { // 等会查询到
o.setName("笨蛋");
o.setTags(Arrays.asList("论坛", "蔬菜"));
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(DateUtils.buildTime(2022, 2, 8));
});
sensitiveWordMapper.insert(dbSensitiveWord);
// 测试 name 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setName("傻瓜")));
// 测试 tags 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setTags(Arrays.asList("短信", "日用品"))));
// 测试 createTime 不匹配
sensitiveWordMapper.insert(cloneIgnoreId(dbSensitiveWord, o -> o.setCreateTime(DateUtils.buildTime(2022, 2, 16))));
// 准备参数
SensitiveWordExportReqVO reqVO = new SensitiveWordExportReqVO();
reqVO.setName("");
reqVO.setTag("论坛");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(DateUtils.buildTime(2022, 2, 1));
reqVO.setEndCreateTime(DateUtils.buildTime(2022, 2, 12));
// 调用
List<SensitiveWordDO> list = sensitiveWordService.getSensitiveWordList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbSensitiveWord, list.get(0));
}
@Test
public void testValidateText_noTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用
List<String> result = sensitiveWordService.validateText(text, null);
// 断言
assertEquals(Arrays.asList("傻瓜", "笨蛋"), result);
}
@Test
public void testValidateText_hasTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用
List<String> result = sensitiveWordService.validateText(text, singletonList("论坛"));
// 断言
assertEquals(singletonList("傻瓜"), result);
}
@Test
public void testIsTestValid_noTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用,断言
assertFalse(sensitiveWordService.isTextValid(text, null));
}
@Test
public void testIsTestValid_hasTag() {
testInitLocalCache();
// 准备参数
String text = "你是傻瓜,你是笨蛋";
// 调用,断言
assertFalse(sensitiveWordService.isTextValid(text, singletonList("论坛")));
}
}

View File

@ -0,0 +1,196 @@
package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 java.util.Date;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import(SmsChannelServiceImpl.class)
public class SmsChannelServiceTest extends BaseDbUnitTest {
@Resource
private SmsChannelServiceImpl smsChannelService;
@Resource
private SmsChannelMapper smsChannelMapper;
@MockBean
private SmsClientFactory smsClientFactory;
@MockBean
private SmsTemplateService smsTemplateService;
@MockBean
private SmsProducer smsProducer;
@Test
public void testInitLocalCache_success() {
// mock 数据
SmsChannelDO smsChannelDO01 = randomSmsChannelDO();
smsChannelMapper.insert(smsChannelDO01);
SmsChannelDO smsChannelDO02 = randomSmsChannelDO();
smsChannelMapper.insert(smsChannelDO02);
// 调用
smsChannelService.initSmsClients();
// 校验 maxUpdateTime 属性
Date maxUpdateTime = (Date) BeanUtil.getFieldValue(smsChannelService, "maxUpdateTime");
assertEquals(max(smsChannelDO01.getUpdateTime(), smsChannelDO02.getUpdateTime()), maxUpdateTime);
// 校验调用
verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
argThat(properties -> isPojoEquals(smsChannelDO01, properties)));
verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
argThat(properties -> isPojoEquals(smsChannelDO02, properties)));
}
@Test
public void testCreateSmsChannel_success() {
// 准备参数
SmsChannelCreateReqVO reqVO = randomPojo(SmsChannelCreateReqVO.class, o -> o.setStatus(randomCommonStatus()));
// 调用
Long smsChannelId = smsChannelService.createSmsChannel(reqVO);
// 断言
assertNotNull(smsChannelId);
// 校验记录的属性是否正确
SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
assertPojoEquals(reqVO, smsChannel);
// 校验调用
verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();
}
@Test
public void testUpdateSmsChannel_success() {
// mock 数据
SmsChannelDO dbSmsChannel = randomSmsChannelDO();
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
SmsChannelUpdateReqVO reqVO = randomPojo(SmsChannelUpdateReqVO.class, o -> {
o.setId(dbSmsChannel.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
o.setCallbackUrl(randomString());
});
// 调用
smsChannelService.updateSmsChannel(reqVO);
// 校验是否更新正确
SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, smsChannel);
// 校验调用
verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();
}
@Test
public void testUpdateSmsChannel_notExists() {
// 准备参数
SmsChannelUpdateReqVO reqVO = randomPojo(SmsChannelUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> smsChannelService.updateSmsChannel(reqVO), SMS_CHANNEL_NOT_EXISTS);
}
@Test
public void testDeleteSmsChannel_success() {
// mock 数据
SmsChannelDO dbSmsChannel = randomSmsChannelDO();
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSmsChannel.getId();
// 调用
smsChannelService.deleteSmsChannel(id);
// 校验数据不存在了
assertNull(smsChannelMapper.selectById(id));
// 校验调用
verify(smsProducer, times(1)).sendSmsChannelRefreshMessage();
}
@Test
public void testDeleteSmsChannel_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_NOT_EXISTS);
}
@Test
public void testDeleteSmsChannel_hasChildren() {
// mock 数据
SmsChannelDO dbSmsChannel = randomSmsChannelDO();
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSmsChannel.getId();
// mock 方法
when(smsTemplateService.countByChannelId(eq(id))).thenReturn(10L);
// 调用, 并断言异常
assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_HAS_CHILDREN);
}
@Test
public void testGetSmsChannelPage() {
// mock 数据
SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到
o.setSignature("芋道源码");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
});
smsChannelMapper.insert(dbSmsChannel);
// 测试 signature 不匹配
smsChannelMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码")));
// 测试 status 不匹配
smsChannelMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
smsChannelMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 准备参数
SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO();
reqVO.setSignature("芋道");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2020, 12, 1));
reqVO.setEndCreateTime(buildTime(2020, 12, 24));
// 调用
PageResult<SmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSmsChannel, pageResult.getList().get(0));
}
// ========== 随机对象 ==========
@SafeVarargs
private static SmsChannelDO randomSmsChannelDO(Consumer<SmsChannelDO>... consumers) {
Consumer<SmsChannelDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(SmsChannelDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,243 @@
package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper;
import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomBoolean;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@Import(SmsLogServiceImpl.class)
public class SmsLogServiceTest extends BaseDbUnitTest {
@Resource
private SmsLogServiceImpl smsLogService;
@Resource
private SmsLogMapper smsLogMapper;
@Test
public void testGetSmsLogPage() {
// mock 数据
SmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到
o.setChannelId(1L);
o.setTemplateId(10L);
o.setMobile("15601691300");
o.setSendStatus(SmsSendStatusEnum.INIT.getStatus());
o.setSendTime(buildTime(2020, 11, 11));
o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus());
o.setReceiveTime(buildTime(2021, 11, 11));
});
smsLogMapper.insert(dbSmsLog);
// 测试 channelId 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setChannelId(2L)));
// 测试 templateId 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setTemplateId(20L)));
// 测试 mobile 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setMobile("18818260999")));
// 测试 sendStatus 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus())));
// 测试 sendTime 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12))));
// 测试 receiveStatus 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setReceiveStatus(SmsReceiveStatusEnum.SUCCESS.getStatus())));
// 测试 receiveTime 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12))));
// 准备参数
SmsLogPageReqVO reqVO = new SmsLogPageReqVO();
reqVO.setChannelId(1L);
reqVO.setTemplateId(10L);
reqVO.setMobile("156");
reqVO.setSendStatus(SmsSendStatusEnum.INIT.getStatus());
reqVO.setBeginSendTime(buildTime(2020, 11, 1));
reqVO.setEndSendTime(buildTime(2020, 11, 30));
reqVO.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus());
reqVO.setBeginReceiveTime(buildTime(2021, 11, 1));
reqVO.setEndReceiveTime(buildTime(2021, 11, 30));
// 调用
PageResult<SmsLogDO> pageResult = smsLogService.getSmsLogPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSmsLog, pageResult.getList().get(0));
}
@Test
public void testGetSmsLogList() {
// mock 数据
SmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到
o.setChannelId(1L);
o.setTemplateId(10L);
o.setMobile("15601691300");
o.setSendStatus(SmsSendStatusEnum.INIT.getStatus());
o.setSendTime(buildTime(2020, 11, 11));
o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus());
o.setReceiveTime(buildTime(2021, 11, 11));
});
smsLogMapper.insert(dbSmsLog);
// 测试 channelId 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setChannelId(2L)));
// 测试 templateId 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setTemplateId(20L)));
// 测试 mobile 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setMobile("18818260999")));
// 测试 sendStatus 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus())));
// 测试 sendTime 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12))));
// 测试 receiveStatus 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setReceiveStatus(SmsReceiveStatusEnum.SUCCESS.getStatus())));
// 测试 receiveTime 不匹配
smsLogMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12))));
// 准备参数
SmsLogExportReqVO reqVO = new SmsLogExportReqVO();
reqVO.setChannelId(1L);
reqVO.setTemplateId(10L);
reqVO.setMobile("156");
reqVO.setSendStatus(SmsSendStatusEnum.INIT.getStatus());
reqVO.setBeginSendTime(buildTime(2020, 11, 1));
reqVO.setEndSendTime(buildTime(2020, 11, 30));
reqVO.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus());
reqVO.setBeginReceiveTime(buildTime(2021, 11, 1));
reqVO.setEndReceiveTime(buildTime(2021, 11, 30));
// 调用
List<SmsLogDO> list = smsLogService.getSmsLogList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbSmsLog, list.get(0));
}
@Test
public void testCreateSmsLog() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
Boolean isSend = randomBoolean();
SmsTemplateDO templateDO = randomPojo(SmsTemplateDO.class,
o -> o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()));
String templateContent = randomString();
Map<String, Object> templateParams = randomTemplateParams();
// mock 方法
// 调用
Long logId = smsLogService.createSmsLog(mobile, userId, userType, isSend,
templateDO, templateContent, templateParams);
// 断言
SmsLogDO logDO = smsLogMapper.selectById(logId);
assertEquals(isSend ? SmsSendStatusEnum.INIT.getStatus() : SmsSendStatusEnum.IGNORE.getStatus(),
logDO.getSendStatus());
assertEquals(mobile, logDO.getMobile());
assertEquals(userType, logDO.getUserType());
assertEquals(userId, logDO.getUserId());
assertEquals(templateDO.getId(), logDO.getTemplateId());
assertEquals(templateDO.getCode(), logDO.getTemplateCode());
assertEquals(templateDO.getType(), logDO.getTemplateType());
assertEquals(templateDO.getChannelId(), logDO.getChannelId());
assertEquals(templateDO.getChannelCode(), logDO.getChannelCode());
assertEquals(templateContent, logDO.getTemplateContent());
assertEquals(templateParams, logDO.getTemplateParams());
assertEquals(SmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus());
}
@Test
public void testUpdateSmsSendResult() {
// mock 数据
SmsLogDO dbSmsLog = randomSmsLogDO(
o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus()));
smsLogMapper.insert(dbSmsLog);
// 准备参数
Long id = dbSmsLog.getId();
Integer sendCode = randomInteger();
String sendMsg = randomString();
String apiSendCode = randomString();
String apiSendMsg = randomString();
String apiRequestId = randomString();
String apiSerialNo = randomString();
// 调用
smsLogService.updateSmsSendResult(id, sendCode, sendMsg,
apiSendCode, apiSendMsg, apiRequestId, apiSerialNo);
// 断言
dbSmsLog = smsLogMapper.selectById(id);
assertEquals(CommonResult.isSuccess(sendCode) ? SmsSendStatusEnum.SUCCESS.getStatus()
: SmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus());
assertNotNull(dbSmsLog.getSendTime());
assertEquals(sendMsg, dbSmsLog.getSendMsg());
assertEquals(apiSendCode, dbSmsLog.getApiSendCode());
assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg());
assertEquals(apiRequestId, dbSmsLog.getApiRequestId());
assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo());
}
@Test
public void testUpdateSmsReceiveResult() {
// mock 数据
SmsLogDO dbSmsLog = randomSmsLogDO(
o -> o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()));
smsLogMapper.insert(dbSmsLog);
// 准备参数
Long id = dbSmsLog.getId();
Boolean success = randomBoolean();
Date receiveTime = randomDate();
String apiReceiveCode = randomString();
String apiReceiveMsg = randomString();
// 调用
smsLogService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg);
// 断言
dbSmsLog = smsLogMapper.selectById(id);
assertEquals(success ? SmsReceiveStatusEnum.SUCCESS.getStatus()
: SmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus());
assertEquals(receiveTime, dbSmsLog.getReceiveTime());
assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode());
assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg());
}
// ========== 随机对象 ==========
@SafeVarargs
private static SmsLogDO randomSmsLogDO(Consumer<SmsLogDO>... consumers) {
Consumer<SmsLogDO> consumer = (o) -> {
o.setTemplateParams(randomTemplateParams());
o.setTemplateType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 templateType 的范围
o.setUserType(randomEle(UserTypeEnum.values()).getValue()); // 保证 userType 的范围
o.setSendStatus(randomEle(SmsSendStatusEnum.values()).getStatus()); // 保证 sendStatus 的范围
o.setReceiveStatus(randomEle(SmsReceiveStatusEnum.values()).getStatus()); // 保证 receiveStatus 的范围
};
return randomPojo(SmsLogDO.class, ArrayUtils.append(consumer, consumers));
}
private static Map<String, Object> randomTemplateParams() {
return MapUtil.<String, Object>builder().put(randomString(), randomString())
.put(randomString(), randomString()).build();
}
}

View File

@ -0,0 +1,193 @@
package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
public class SmsSendServiceTest extends BaseMockitoUnitTest {
@InjectMocks
private SmsSendServiceImpl smsService;
@Mock
private SmsTemplateService smsTemplateService;
@Mock
private SmsLogService smsLogService;
@Mock
private SmsProducer smsProducer;
@Mock
private SmsClientFactory smsClientFactory;
/**
* 发送成功,当短信模板开启时
*/
@Test
public void testSendSingleSms_successWhenSmsTemplateEnable() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock SmsTemplateService 的方法
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock SmsLogService 的方法
Long smsLogId = randomLongId();
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template),
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
verify(smsProducer, times(1)).sendSmsSendMessage(eq(smsLogId), eq(mobile),
eq(template.getChannelId()), eq(template.getApiTemplateId()),
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
}
/**
* 发送成功,当短信模板关闭时
*/
@Test
public void testSendSingleSms_successWhenSmsTemplateDisable() {
// 准备参数
String mobile = randomString();
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock SmsTemplateService 的方法
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock SmsLogService 的方法
Long smsLogId = randomLongId();
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template),
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
verify(smsProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(),
anyLong(), any(), anyList());
}
@Test
public void testCheckSmsTemplateValid_notExists() {
// 准备参数
String templateCode = randomString();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.checkSmsTemplateValid(templateCode),
SMS_SEND_TEMPLATE_NOT_EXISTS);
}
@Test
public void testBuildTemplateParams_paramMiss() {
// 准备参数
SmsTemplateDO template = randomPojo(SmsTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
Map<String, Object> templateParams = new HashMap<>();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.buildTemplateParams(template, templateParams),
SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code");
}
@Test
public void testCheckMobile_notExists() {
// 准备参数
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.checkMobile(null),
SMS_SEND_MOBILE_NOT_EXISTS);
}
@Test
@SuppressWarnings("unchecked")
public void testDoSendSms() {
// 准备参数
SmsSendMessage message = randomPojo(SmsSendMessage.class);
// mock SmsClientFactory 的方法
SmsClient smsClient = spy(SmsClient.class);
when(smsClientFactory.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
// mock SmsClient 的方法
SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()),
eq(message.getTemplateParams()))).thenReturn(sendResult);
// 调用
smsService.doSendSms(message);
// 断言
verify(smsLogService, times(1)).updateSmsSendResult(eq(message.getLogId()),
eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()),
eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo()));
}
@Test
public void testReceiveSmsStatus() throws Throwable {
// 准备参数
String channelCode = randomString();
String text = randomString();
// mock SmsClientFactory 的方法
SmsClient smsClient = spy(SmsClient.class);
when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient);
// mock SmsClient 的方法
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
// 调用
smsService.receiveSmsStatus(channelCode, text);
// 断言
receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()),
eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode())));
}
}

View File

@ -0,0 +1,374 @@
package cn.iocoder.yudao.module.system.service.sms;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import com.google.common.collect.Lists;
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 java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.bean.BeanUtil.getFieldValue;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
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.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import(SmsTemplateServiceImpl.class)
public class SmsTemplateServiceTest extends BaseDbUnitTest {
@Resource
private SmsTemplateServiceImpl smsTemplateService;
@Resource
private SmsTemplateMapper smsTemplateMapper;
@MockBean
private SmsChannelService smsChannelService;
@MockBean
private SmsClientFactory smsClientFactory;
@MockBean
private SmsClient smsClient;
@MockBean
private SmsProducer smsProducer;
@Test
@SuppressWarnings("unchecked")
void testInitLocalCache() {
// mock 数据
SmsTemplateDO smsTemplate01 = randomSmsTemplateDO();
smsTemplateMapper.insert(smsTemplate01);
SmsTemplateDO smsTemplate02 = randomSmsTemplateDO();
smsTemplateMapper.insert(smsTemplate02);
// 调用
smsTemplateService.initLocalCache();
// 断言 deptCache 缓存
Map<String, SmsTemplateDO> smsTemplateCache = (Map<String, SmsTemplateDO>) getFieldValue(smsTemplateService, "smsTemplateCache");
assertEquals(2, smsTemplateCache.size());
assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode()));
assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode()));
// 断言 maxUpdateTime 缓存
Date maxUpdateTime = (Date) getFieldValue(smsTemplateService, "maxUpdateTime");
assertEquals(max(smsTemplate01.getUpdateTime(), smsTemplate02.getUpdateTime()), maxUpdateTime);
}
@Test
public void testParseTemplateContentParams() {
// 准备参数
String content = "正在进行登录操作{operation},您的验证码是{code}";
// mock 方法
// 调用
List<String> params = smsTemplateService.parseTemplateContentParams(content);
// 断言
assertEquals(Lists.newArrayList("operation", "code"), params);
}
@Test
@SuppressWarnings("unchecked")
public void testCreateSmsTemplate_success() {
// 准备参数
SmsTemplateCreateReqVO reqVO = randomPojo(SmsTemplateCreateReqVO.class, o -> {
o.setContent("正在进行登录操作{operation},您的验证码是{code}");
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围
});
// mock Channel 的方法
SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> {
o.setId(reqVO.getChannelId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态
});
when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
// mock 获得 API 短信模板成功
when(smsClientFactory.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
// 调用
Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO);
// 断言
assertNotNull(smsTemplateId);
// 校验记录的属性是否正确
SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(smsTemplateId);
assertPojoEquals(reqVO, smsTemplate);
assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams());
assertEquals(channelDO.getCode(), smsTemplate.getChannelCode());
// 校验调用
verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();
}
@Test
@SuppressWarnings("unchecked")
public void testUpdateSmsTemplate_success() {
// mock 数据
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
SmsTemplateUpdateReqVO reqVO = randomPojo(SmsTemplateUpdateReqVO.class, o -> {
o.setId(dbSmsTemplate.getId()); // 设置更新的 ID
o.setContent("正在进行登录操作{operation},您的验证码是{code}");
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围
});
// mock 方法
SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> {
o.setId(reqVO.getChannelId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态
});
when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
// mock 获得 API 短信模板成功
when(smsClientFactory.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
// 调用
smsTemplateService.updateSmsTemplate(reqVO);
// 校验是否更新正确
SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, smsTemplate);
assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams());
assertEquals(channelDO.getCode(), smsTemplate.getChannelCode());
// 校验调用
verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();
}
@Test
public void testUpdateSmsTemplate_notExists() {
// 准备参数
SmsTemplateUpdateReqVO reqVO = randomPojo(SmsTemplateUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> smsTemplateService.updateSmsTemplate(reqVO), SMS_TEMPLATE_NOT_EXISTS);
}
@Test
public void testDeleteSmsTemplate_success() {
// mock 数据
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSmsTemplate.getId();
// 调用
smsTemplateService.deleteSmsTemplate(id);
// 校验数据不存在了
assertNull(smsTemplateMapper.selectById(id));
// 校验调用
verify(smsProducer, times(1)).sendSmsTemplateRefreshMessage();
}
@Test
public void testDeleteSmsTemplate_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> smsTemplateService.deleteSmsTemplate(id), SMS_TEMPLATE_NOT_EXISTS);
}
@Test
public void testGetSmsTemplatePage() {
// mock 数据
SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到
o.setType(SmsTemplateTypeEnum.PROMOTION.getType());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCode("tudou");
o.setContent("芋道源码");
o.setApiTemplateId("yunai");
o.setChannelId(1L);
o.setCreateTime(buildTime(2021, 11, 11));
});
smsTemplateMapper.insert(dbSmsTemplate);
// 测试 type 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType())));
// 测试 status 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 code 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma")));
// 测试 content 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码")));
// 测试 apiTemplateId 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai")));
// 测试 channelId 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L)));
// 测试 createTime 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12))));
// 准备参数
SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO();
reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCode("tu");
reqVO.setContent("芋道");
reqVO.setApiTemplateId("yu");
reqVO.setChannelId(1L);
reqVO.setBeginCreateTime(buildTime(2021, 11, 1));
reqVO.setEndCreateTime(buildTime(2021, 12, 1));
// 调用
PageResult<SmsTemplateDO> pageResult = smsTemplateService.getSmsTemplatePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0));
}
@Test
public void testGetSmsTemplateList() {
// mock 数据
SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到
o.setType(SmsTemplateTypeEnum.PROMOTION.getType());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCode("tudou");
o.setContent("芋道源码");
o.setApiTemplateId("yunai");
o.setChannelId(1L);
o.setCreateTime(buildTime(2021, 11, 11));
});
smsTemplateMapper.insert(dbSmsTemplate);
// 测试 type 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType())));
// 测试 status 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 code 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma")));
// 测试 content 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码")));
// 测试 apiTemplateId 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai")));
// 测试 channelId 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L)));
// 测试 createTime 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12))));
// 准备参数
SmsTemplateExportReqVO reqVO = new SmsTemplateExportReqVO();
reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCode("tu");
reqVO.setContent("芋道");
reqVO.setApiTemplateId("yu");
reqVO.setChannelId(1L);
reqVO.setBeginCreateTime(buildTime(2021, 11, 1));
reqVO.setEndCreateTime(buildTime(2021, 12, 1));
// 调用
List<SmsTemplateDO> list = smsTemplateService.getSmsTemplateList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbSmsTemplate, list.get(0));
}
@Test
public void testCheckSmsChannel_success() {
// 准备参数
Long channelId = randomLongId();
// mock 方法
SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> {
o.setId(channelId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态
});
when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO);
// 调用
SmsChannelDO returnChannelDO = smsTemplateService.checkSmsChannel(channelId);
// 断言
assertPojoEquals(returnChannelDO, channelDO);
}
@Test
public void testCheckSmsChannel_notExists() {
// 准备参数
Long channelId = randomLongId();
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsChannel(channelId),
SMS_CHANNEL_NOT_EXISTS);
}
@Test
public void testCheckSmsChannel_disable() {
// 准备参数
Long channelId = randomLongId();
// mock 方法
SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> {
o.setId(channelId);
o.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 保证 status 禁用,触发失败
});
when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO);
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsChannel(channelId),
SMS_CHANNEL_DISABLE);
}
@Test
public void testCheckDictDataValueUnique_success() {
// 调用,成功
smsTemplateService.checkSmsTemplateCodeDuplicate(randomLongId(), randomString());
}
@Test
public void testCheckSmsTemplateCodeDuplicate_valueDuplicateForCreate() {
// 准备参数
String code = randomString();
// mock 数据
smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsTemplateCodeDuplicate(null, code),
SMS_TEMPLATE_CODE_DUPLICATE, code);
}
@Test
public void testCheckDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String code = randomString();
// mock 数据
smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code)));
// 调用,校验异常
assertServiceException(() -> smsTemplateService.checkSmsTemplateCodeDuplicate(id, code),
SMS_TEMPLATE_CODE_DUPLICATE, code);
}
// ========== 随机对象 ==========
@SafeVarargs
private static SmsTemplateDO randomSmsTemplateDO(Consumer<SmsTemplateDO>... consumers) {
Consumer<SmsTemplateDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围
};
return randomPojo(SmsTemplateDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,256 @@
package cn.iocoder.yudao.module.system.service.social;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import com.xkcoding.justauth.AuthRequestFactory;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomLong;
import static cn.hutool.core.util.RandomUtil.randomString;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
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.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
@Import(SocialUserServiceImpl.class)
public class SocialUserServiceTest extends BaseDbAndRedisUnitTest {
@Resource
private SocialUserServiceImpl socialUserService;
@Resource
private SocialUserMapper socialUserMapper;
@Resource
private SocialUserBindMapper socialUserBindMapper;
@MockBean
private AuthRequestFactory authRequestFactory;
@Test
public void testGetAuthorizeUrl() {
try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) {
// 准备参数
Integer type = SocialTypeEnum.WECHAT_MP.getType();
String redirectUri = "sss";
// mock 获得对应的 AuthRequest 实现
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// mock 方法
authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman");
when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy");
// 调用
String url = socialUserService.getAuthorizeUrl(type, redirectUri);
// 断言
assertEquals("https://www.iocoder.cn/?redirect_uri=sss", url);
}
}
@Test
public void testAuthSocialUser_exists() {
// 准备参数
Integer type = SocialTypeEnum.GITEE.getType();
String code = "tudou";
String state = "yuanma";
// mock 方法
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
socialUserMapper.insert(socialUser);
// 调用
SocialUserDO result = socialUserService.authSocialUser(type, code, state);
// 断言
assertPojoEquals(socialUser, result);
}
@Test
public void testAuthSocialUser_authFailure() {
// 准备参数
Integer type = SocialTypeEnum.GITEE.getType();
// mock 方法
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(anyString())).thenReturn(authRequest);
AuthResponse<?> authResponse = new AuthResponse<>(0, "模拟失败", null);
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
// 调用并断言
assertServiceException(
() -> socialUserService.authSocialUser(type, randomString(10), randomString(10)),
SOCIAL_USER_AUTH_FAILURE, "模拟失败");
}
@Test
public void testAuthSocialUser_insert() {
// 准备参数
Integer type = SocialTypeEnum.GITEE.getType();
String code = "tudou";
String state = "yuanma";
// mock 方法
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
AuthUser authUser = randomPojo(AuthUser.class);
AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
// 调用
SocialUserDO result = socialUserService.authSocialUser(type, code, state);
// 断言
assertBindSocialUser(type, result, authResponse.getData());
assertEquals(code, result.getCode());
assertEquals(state, result.getState());
}
@Test
public void testAuthSocialUser_update() {
// 准备参数
Integer type = SocialTypeEnum.GITEE.getType();
String code = "tudou";
String state = "yuanma";
// mock 数据
socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(type).setOpenid("test_openid"));
// mock 方法
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
AuthUser authUser = randomPojo(AuthUser.class);
authUser.getToken().setOpenId("test_openid");
AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
// 调用
SocialUserDO result = socialUserService.authSocialUser(type, code, state);
// 断言
assertBindSocialUser(type, result, authResponse.getData());
assertEquals(code, result.getCode());
assertEquals(state, result.getState());
}
private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) {
assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());
assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());
assertEquals(authUser.getNickname(), socialUser.getNickname());
assertEquals(authUser.getAvatar(), socialUser.getAvatar());
assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());
assertEquals(type, socialUser.getType());
assertEquals(authUser.getUuid(), socialUser.getOpenid());
}
@Test
public void testGetSocialUserList() {
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// mock 获得社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(SocialTypeEnum.GITEE.getType());
socialUserMapper.insert(socialUser); // 可被查到
socialUserMapper.insert(randomPojo(SocialUserDO.class)); // 不可被查到
// mock 获得绑定
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 可被查询到
.setUserId(userId).setUserType(userType).setSocialType(SocialTypeEnum.GITEE.getType())
.setSocialUserId(socialUser.getId()));
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 不可被查询到
.setUserId(2L).setUserType(userType).setSocialType(SocialTypeEnum.DINGTALK.getType()));
// 调用
List<SocialUserDO> result = socialUserService.getSocialUserList(userId, userType);
// 断言
assertEquals(1, result.size());
assertPojoEquals(socialUser, result.get(0));
}
@Test
public void testBindSocialUser() {
// 准备参数
SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO()
.setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
.setType(SocialTypeEnum.GITEE.getType()).setCode("test_code").setState("test_state");
// mock 数据:获得社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(reqDTO.getType())
.setCode(reqDTO.getCode()).setState(reqDTO.getState());
socialUserMapper.insert(socialUser);
// mock 数据:用户可能之前已经绑定过该社交类型
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
.setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(-1L));
// mock 数据:社交用户可能之前绑定过别的用户
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserType(UserTypeEnum.ADMIN.getValue())
.setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId()));
// 调用
socialUserService.bindSocialUser(reqDTO);
// 断言
List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList();
assertEquals(1, socialUserBinds.size());
}
@Test
public void testUnbindSocialUser_success() {
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
Integer type = SocialTypeEnum.GITEE.getType();
String openid = "test_openid";
// mock 数据:社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setOpenid(openid);
socialUserMapper.insert(socialUser);
// mock 数据:社交绑定关系
SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType)
.setUserId(userId).setSocialType(type);
socialUserBindMapper.insert(socialUserBind);
// 调用
socialUserService.unbindSocialUser(userId, userType, type, openid);
// 断言
assertEquals(0, socialUserBindMapper.selectCount(null).intValue());
}
@Test
public void testUnbindSocialUser_notFound() {
// 调用,并断言
assertServiceException(
() -> socialUserService.unbindSocialUser(randomLong(), UserTypeEnum.ADMIN.getValue(),
SocialTypeEnum.GITEE.getType(), "test_openid"),
SOCIAL_USER_NOT_FOUND);
}
@Test
public void testGetBindUserId() {
// 准备参数
Integer userType = UserTypeEnum.ADMIN.getValue();
Integer type = SocialTypeEnum.GITEE.getType();
String code = "tudou";
String state = "yuanma";
// mock 社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
socialUserMapper.insert(socialUser);
// mock 社交用户的绑定
Long userId = randomLong();
SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId)
.setSocialType(type).setSocialUserId(socialUser.getId());
socialUserBindMapper.insert(socialUserBind);
// 调用
Long result = socialUserService.getBindUserId(userType, type, code, state);
// 断言
assertEquals(userId, result);
}
}

View File

@ -0,0 +1,203 @@
package cn.iocoder.yudao.module.system.service.tenant;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
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.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* {@link TenantPackageServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(TenantPackageServiceImpl.class)
public class TenantPackageServiceImplTest extends BaseDbUnitTest {
@Resource
private TenantPackageServiceImpl tenantPackageService;
@Resource
private TenantPackageMapper tenantPackageMapper;
@MockBean
private TenantService tenantService;
@Test
public void testCreateTenantPackage_success() {
// 准备参数
TenantPackageCreateReqVO reqVO = randomPojo(TenantPackageCreateReqVO.class);
// 调用
Long tenantPackageId = tenantPackageService.createTenantPackage(reqVO);
// 断言
assertNotNull(tenantPackageId);
// 校验记录的属性是否正确
TenantPackageDO tenantPackage = tenantPackageMapper.selectById(tenantPackageId);
assertPojoEquals(reqVO, tenantPackage);
}
@Test
public void testUpdateTenantPackage_success() {
// mock 数据
TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class);
tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据
// 准备参数
TenantPackageUpdateReqVO reqVO = randomPojo(TenantPackageUpdateReqVO.class, o -> {
o.setId(dbTenantPackage.getId()); // 设置更新的 ID
});
// mock 方法
Long tenantId01 = randomLongId();
Long tenantId02 = randomLongId();
when(tenantService.getTenantListByPackageId(eq(reqVO.getId()))).thenReturn(
asList(randomPojo(TenantDO.class, o -> o.setId(tenantId01)),
randomPojo(TenantDO.class, o -> o.setId(tenantId02))));
// 调用
tenantPackageService.updateTenantPackage(reqVO);
// 校验是否更新正确
TenantPackageDO tenantPackage = tenantPackageMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, tenantPackage);
// 校验调用租户的菜单
verify(tenantService).updateTenantRoleMenu(eq(tenantId01), eq(reqVO.getMenuIds()));
verify(tenantService).updateTenantRoleMenu(eq(tenantId02), eq(reqVO.getMenuIds()));
}
@Test
public void testUpdateTenantPackage_notExists() {
// 准备参数
TenantPackageUpdateReqVO reqVO = randomPojo(TenantPackageUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> tenantPackageService.updateTenantPackage(reqVO), TENANT_PACKAGE_NOT_EXISTS);
}
@Test
public void testDeleteTenantPackage_success() {
// mock 数据
TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class);
tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbTenantPackage.getId();
// mock 租户未使用该套餐
when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(0L);
// 调用
tenantPackageService.deleteTenantPackage(id);
// 校验数据不存在了
assertNull(tenantPackageMapper.selectById(id));
}
@Test
public void testDeleteTenantPackage_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS);
}
@Test
public void testDeleteTenantPackage_used() {
// mock 数据
TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class);
tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbTenantPackage.getId();
// mock 租户在使用该套餐
when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(1L);
// 调用, 并断言异常
assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_USED);
}
@Test
public void testGetTenantPackagePage() {
// mock 数据
TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, o -> { // 等会查询到
o.setName("芋道源码");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setRemark("源码解析");
o.setCreateTime(buildTime(2022, 10, 10));
});
tenantPackageMapper.insert(dbTenantPackage);
// 测试 name 不匹配
tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setName("源码")));
// 测试 status 不匹配
tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 remark 不匹配
tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setRemark("解析")));
// 测试 createTime 不匹配
tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setCreateTime(buildTime(2022, 11, 11))));
// 准备参数
TenantPackagePageReqVO reqVO = new TenantPackagePageReqVO();
reqVO.setName("芋道");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setRemark("源码");
reqVO.setBeginCreateTime(buildTime(2022, 10, 9));
reqVO.setEndCreateTime(buildTime(2022, 10, 11));
// 调用
PageResult<TenantPackageDO> pageResult = tenantPackageService.getTenantPackagePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbTenantPackage, pageResult.getList().get(0));
}
@Test
public void testValidTenantPackage_success() {
// mock 数据
TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class,
o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据
// 调用
TenantPackageDO result = tenantPackageService.validTenantPackage(dbTenantPackage.getId());
// 断言
assertPojoEquals(dbTenantPackage, result);
}
@Test
public void testValidTenantPackage_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> tenantPackageService.validTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS);
}
@Test
public void testValidTenantPackage_disable() {
// mock 数据
TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class,
o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据
// 调用, 并断言异常
assertServiceException(() -> tenantPackageService.validTenantPackage(dbTenantPackage.getId()),
TENANT_PACKAGE_DISABLE, dbTenantPackage.getName());
}
}

View File

@ -0,0 +1,523 @@
package cn.iocoder.yudao.module.system.service.tenant;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.mq.producer.tenant.TenantProducer;
import cn.iocoder.yudao.module.system.service.permission.MenuService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.permission.RoleService;
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
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 java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
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.*;
import static cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO.PACKAGE_ID_SYSTEM;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* {@link TenantServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(TenantServiceImpl.class)
public class TenantServiceImplTest extends BaseDbUnitTest {
@Resource
private TenantServiceImpl tenantService;
@Resource
private TenantMapper tenantMapper;
@MockBean
private TenantProperties tenantProperties;
@MockBean
private TenantPackageService tenantPackageService;
@MockBean
private AdminUserService userService;
@MockBean
private RoleService roleService;
@MockBean
private MenuService menuService;
@MockBean
private PermissionService permissionService;
@MockBean
private TenantProducer tenantProducer;
@BeforeEach
public void setUp() {
// 清理缓存
ReflectUtil.setFieldValue(tenantService, "tenantCache", Collections.emptyMap());
ReflectUtil.setFieldValue(tenantService, "maxUpdateTime", null);
// 清理租户上下文
TenantContextHolder.clear();
}
@Test
public void testInitLocalCache() {
// mock 数据
TenantDO tenantDO1 = randomPojo(TenantDO.class);
tenantMapper.insert(tenantDO1);
TenantDO tenantDO2 = randomPojo(TenantDO.class);
tenantMapper.insert(tenantDO2);
// 调用
tenantService.initLocalCache();
// 断言 tenantCache 缓存
Map<Long, TenantDO> tenantCache = tenantService.getTenantCache();
assertEquals(2, tenantCache.size());
assertPojoEquals(tenantDO1, tenantCache.get(tenantDO1.getId()));
assertPojoEquals(tenantDO2, tenantCache.get(tenantDO2.getId()));
// 断言 maxUpdateTime 缓存
assertEquals(max(tenantDO1.getUpdateTime(), tenantDO2.getUpdateTime()), tenantService.getMaxUpdateTime());
}
@Test
public void testGetTenantIds() {
// mock 数据
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L));
tenantMapper.insert(tenant);
tenantService.initLocalCache();
// 调用,并断言业务异常
List<Long> result = tenantService.getTenantIds();
assertEquals(Collections.singletonList(1L), result);
}
@Test
public void testValidTenant_notExists() {
assertServiceException(() -> tenantService.validTenant(randomLongId()), TENANT_NOT_EXISTS);
}
@Test
public void testValidTenant_disable() {
// mock 数据
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.DISABLE.getStatus()));
tenantMapper.insert(tenant);
tenantService.initLocalCache();
// 调用,并断言业务异常
assertServiceException(() -> tenantService.validTenant(1L), TENANT_DISABLE, tenant.getName());
}
@Test
public void testValidTenant_expired() {
// mock 数据
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus())
.setExpireTime(buildTime(2020, 2, 2)));
tenantMapper.insert(tenant);
tenantService.initLocalCache();
// 调用,并断言业务异常
assertServiceException(() -> tenantService.validTenant(1L), TENANT_EXPIRE, tenant.getName());
}
@Test
public void testValidTenant_success() {
// mock 数据
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus())
.setExpireTime(addTime(Duration.ofDays(1))));
tenantMapper.insert(tenant);
tenantService.initLocalCache();
// 调用,并断言业务异常
tenantService.validTenant(1L);
}
@Test
public void testCreateTenant() {
// mock 套餐 100L
TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, o -> o.setId(100L));
when(tenantPackageService.validTenantPackage(eq(100L))).thenReturn(tenantPackage);
// mock 角色 200L
when(roleService.createRole(argThat(role -> {
assertEquals(RoleCodeEnum.TENANT_ADMIN.getName(), role.getName());
assertEquals(RoleCodeEnum.TENANT_ADMIN.getCode(), role.getCode());
assertEquals(0, role.getSort());
assertEquals("系统自动生成", role.getRemark());
return true;
}), eq(RoleTypeEnum.SYSTEM.getType()))).thenReturn(200L);
// mock 用户 300L
when(userService.createUser(argThat(user -> {
assertEquals("yunai", user.getUsername());
assertEquals("yuanma", user.getPassword());
assertEquals("芋道", user.getNickname());
assertEquals("15601691300", user.getMobile());
return true;
}))).thenReturn(300L);
// 准备参数
TenantCreateReqVO reqVO = randomPojo(TenantCreateReqVO.class, o -> {
o.setContactName("芋道");
o.setContactMobile("15601691300");
o.setPackageId(100L);
o.setStatus(randomCommonStatus());
o.setDomain("https://www.iocoder.cn");
o.setUsername("yunai");
o.setPassword("yuanma");
});
// 调用
Long tenantId = tenantService.createTenant(reqVO);
// 断言
assertNotNull(tenantId);
// 校验记录的属性是否正确
TenantDO tenant = tenantMapper.selectById(tenantId);
assertPojoEquals(reqVO, tenant);
assertEquals(300L, tenant.getContactUserId());
// verify 分配权限
verify(permissionService).assignRoleMenu(eq(200L), same(tenantPackage.getMenuIds()));
// verify 分配角色
verify(permissionService).assignUserRole(eq(300L), eq(singleton(200L)));
// verify 发送刷新消息
verify(tenantProducer).sendTenantRefreshMessage();
}
@Test
public void testUpdateTenant_success() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setStatus(randomCommonStatus()));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
// 准备参数
TenantUpdateReqVO reqVO = randomPojo(TenantUpdateReqVO.class, o -> {
o.setId(dbTenant.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
o.setDomain(randomString());
});
// mock 套餐
TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class,
o -> o.setMenuIds(asSet(200L, 201L)));
when(tenantPackageService.validTenantPackage(eq(reqVO.getPackageId()))).thenReturn(tenantPackage);
// mock 所有角色
RoleDO role100 = randomPojo(RoleDO.class, o -> o.setId(100L).setCode(RoleCodeEnum.TENANT_ADMIN.getCode()));
role100.setTenantId(dbTenant.getId());
RoleDO role101 = randomPojo(RoleDO.class, o -> o.setId(101L));
role101.setTenantId(dbTenant.getId());
when(roleService.getRoles(isNull())).thenReturn(asList(role100, role101));
// mock 每个角色的权限
when(permissionService.getRoleMenuIds(eq(101L))).thenReturn(asSet(201L, 202L));
// 调用
tenantService.updateTenant(reqVO);
// 校验是否更新正确
TenantDO tenant = tenantMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, tenant);
// verify 发送刷新消息
verify(tenantProducer).sendTenantRefreshMessage();
// verify 设置角色权限
verify(permissionService).assignRoleMenu(eq(100L), eq(asSet(200L, 201L)));
verify(permissionService).assignRoleMenu(eq(101L), eq(asSet(201L)));
}
@Test
public void testUpdateTenant_notExists() {
// 准备参数
TenantUpdateReqVO reqVO = randomPojo(TenantUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_NOT_EXISTS);
}
@Test
public void testUpdateTenant_system() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
// 准备参数
TenantUpdateReqVO reqVO = randomPojo(TenantUpdateReqVO.class, o -> {
o.setId(dbTenant.getId()); // 设置更新的 ID
});
// 调用,校验业务异常
assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_CAN_NOT_UPDATE_SYSTEM);
}
@Test
public void testDeleteTenant_success() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class,
o -> o.setStatus(randomCommonStatus()));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbTenant.getId();
// 调用
tenantService.deleteTenant(id);
// 校验数据不存在了
assertNull(tenantMapper.selectById(id));
}
@Test
public void testDeleteTenant_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> tenantService.deleteTenant(id), TENANT_NOT_EXISTS);
}
@Test
public void testDeleteTenant_system() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbTenant.getId();
// 调用, 并断言异常
assertServiceException(() -> tenantService.deleteTenant(id), TENANT_CAN_NOT_UPDATE_SYSTEM);
}
@Test
public void testGetTenant() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class);
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbTenant.getId();
// 调用
TenantDO result = tenantService.getTenant(id);
// 校验存在
assertPojoEquals(result, dbTenant);
}
@Test
public void testGetTenantPage() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class, o -> { // 等会查询到
o.setName("芋道源码");
o.setContactName("芋艿");
o.setContactMobile("15601691300");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
});
tenantMapper.insert(dbTenant);
// 测试 name 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setName(randomString())));
// 测试 contactName 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactName(randomString())));
// 测试 contactMobile 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactMobile(randomString())));
// 测试 status 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setCreateTime(buildTime(2021, 12, 12))));
// 准备参数
TenantPageReqVO reqVO = new TenantPageReqVO();
reqVO.setName("芋道");
reqVO.setContactName("");
reqVO.setContactMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2020, 12, 1));
reqVO.setEndCreateTime(buildTime(2020, 12, 24));
// 调用
PageResult<TenantDO> pageResult = tenantService.getTenantPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbTenant, pageResult.getList().get(0));
}
@Test
public void testGetTenantList() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class, o -> { // 等会查询到
o.setName("芋道源码");
o.setContactName("芋艿");
o.setContactMobile("15601691300");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
});
tenantMapper.insert(dbTenant);
// 测试 name 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setName(randomString())));
// 测试 contactName 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactName(randomString())));
// 测试 contactMobile 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactMobile(randomString())));
// 测试 status 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setCreateTime(buildTime(2021, 12, 12))));
// 准备参数
TenantExportReqVO reqVO = new TenantExportReqVO();
reqVO.setName("芋道");
reqVO.setContactName("");
reqVO.setContactMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginCreateTime(buildTime(2020, 12, 1));
reqVO.setEndCreateTime(buildTime(2020, 12, 24));
// 调用
List<TenantDO> list = tenantService.getTenantList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbTenant, list.get(0));
}
@Test
public void testGetTenantByName() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setName("芋道"));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
// 调用
TenantDO result = tenantService.getTenantByName("芋道");
// 校验存在
assertPojoEquals(result, dbTenant);
}
@Test
public void testGetTenantListByPackageId() {
// mock 数据
TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L));
tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据
TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L));
tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据
// 调用
List<TenantDO> result = tenantService.getTenantListByPackageId(1L);
assertEquals(1, result.size());
assertPojoEquals(dbTenant1, result.get(0));
}
@Test
public void testGetTenantCountByPackageId() {
// mock 数据
TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L));
tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据
TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L));
tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据
// 调用
Long count = tenantService.getTenantCountByPackageId(1L);
assertEquals(1, count);
}
@Test
public void testHandleTenantInfo_disable() {
// 准备参数
TenantInfoHandler handler = mock(TenantInfoHandler.class);
// mock 禁用
when(tenantProperties.getEnable()).thenReturn(false);
// 调用
tenantService.handleTenantInfo(handler);
// 断言
verify(handler, never()).handle(any());
}
@Test
public void testHandleTenantInfo_success() {
// 准备参数
TenantInfoHandler handler = mock(TenantInfoHandler.class);
// mock 未禁用
when(tenantProperties.getEnable()).thenReturn(true);
// mock 租户
TenantDO dbTenant = randomPojo(TenantDO.class);
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
TenantContextHolder.setTenantId(dbTenant.getId());
// 调用
tenantService.handleTenantInfo(handler);
// 断言
verify(handler).handle(argThat(argument -> {
assertPojoEquals(dbTenant, argument);
return true;
}));
}
@Test
public void testHandleTenantMenu_disable() {
// 准备参数
TenantMenuHandler handler = mock(TenantMenuHandler.class);
// mock 禁用
when(tenantProperties.getEnable()).thenReturn(false);
// 调用
tenantService.handleTenantMenu(handler);
// 断言
verify(handler, never()).handle(any());
}
@Test // 系统租户的情况
public void testHandleTenantMenu_system() {
// 准备参数
TenantMenuHandler handler = mock(TenantMenuHandler.class);
// mock 未禁用
when(tenantProperties.getEnable()).thenReturn(true);
// mock 租户
TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
TenantContextHolder.setTenantId(dbTenant.getId());
// mock 菜单
when(menuService.getMenus()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)),
randomPojo(MenuDO.class, o -> o.setId(101L))));
// 调用
tenantService.handleTenantMenu(handler);
// 断言
verify(handler).handle(asSet(100L, 101L));
}
@Test // 普通租户的情况
public void testHandleTenantMenu_normal() {
// 准备参数
TenantMenuHandler handler = mock(TenantMenuHandler.class);
// mock 未禁用
when(tenantProperties.getEnable()).thenReturn(true);
// mock 租户
TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(200L));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
TenantContextHolder.setTenantId(dbTenant.getId());
// mock 菜单
when(tenantPackageService.getTenantPackage(eq(200L))).thenReturn(randomPojo(TenantPackageDO.class,
o -> o.setMenuIds(asSet(100L, 101L))));
// 调用
tenantService.handleTenantMenu(handler);
// 断言
verify(handler).handle(asSet(100L, 101L));
}
}

View File

@ -0,0 +1,583 @@
package cn.iocoder.yudao.module.system.service.user;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.dept.PostService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomBytes;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
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.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.assertj.core.util.Lists.newArrayList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@Import(AdminUserServiceImpl.class)
public class UserServiceImplTest extends BaseDbUnitTest {
@Resource
private AdminUserServiceImpl userService;
@Resource
private AdminUserMapper userMapper;
@MockBean
private DeptService deptService;
@MockBean
private PostService postService;
@MockBean
private PermissionService permissionService;
@MockBean
private PasswordEncoder passwordEncoder;
@MockBean
private TenantService tenantService;
@MockBean
private FileApi fileApi;
@Test
public void testCreatUser_success() {
// 准备参数
UserCreateReqVO reqVO = randomPojo(UserCreateReqVO.class, o -> {
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
o.setMobile(randomString());
});
// mock 账户额度充足
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(1));
doNothing().when(tenantService).handleTenantInfo(argThat(handler -> {
handler.handle(tenant);
return true;
}));
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPosts(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("yudaoyuanma");
// 调用
Long userId = userService.createUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user, "password");
assertEquals("yudaoyuanma", user.getPassword());
assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus());
}
@Test
public void testCreatUser_max() {
// 准备参数
UserCreateReqVO reqVO = randomPojo(UserCreateReqVO.class);
// mock 账户额度不足
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(-1));
doNothing().when(tenantService).handleTenantInfo(argThat(handler -> {
handler.handle(tenant);
return true;
}));
// 调用,并断言异常
assertServiceException(() -> userService.createUser(reqVO), USER_COUNT_MAX, -1);
}
@Test
public void testUpdateUser_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
UserUpdateReqVO reqVO = randomPojo(UserUpdateReqVO.class, o -> {
o.setId(dbUser.getId());
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
o.setMobile(randomString());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPosts(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// 调用
userService.updateUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, user);
}
@Test
public void testUpdateUserProfile_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> {
o.setMobile(randomString());
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
});
// 调用
userService.updateUserProfile(userId, reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user);
}
@Test
public void testUpdateUserPassword_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setPassword("encode:tudou"));
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
UserProfileUpdatePasswordReqVO reqVO = randomPojo(UserProfileUpdatePasswordReqVO.class, o -> {
o.setOldPassword("tudou");
o.setNewPassword("yuanma");
});
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
when(passwordEncoder.matches(eq(reqVO.getOldPassword()), eq(dbUser.getPassword()))).thenReturn(true);
// 调用
userService.updateUserPassword(userId, reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals("encode:yuanma", user.getPassword());
}
@Test
public void testUpdateUserAvatar_success() throws Exception {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
byte[] avatarFileBytes = randomBytes(10);
ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
// mock 方法
String avatar = randomString();
when(fileApi.createFile(eq(avatarFileBytes))).thenReturn(avatar);
// 调用
userService.updateUserAvatar(userId, avatarFile);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals(avatar, user.getAvatar());
}
@Test
public void testUpdateUserPassword02_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
String password = "yudao";
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
// 调用
userService.updateUserPassword(userId, password);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals("encode:" + password, user.getPassword());
}
@Test
public void testUpdateUserStatus() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
Integer status = randomCommonStatus();
// 调用
userService.updateUserStatus(userId, status);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals(status, user.getStatus());
}
@Test
public void testDeleteUser_success(){
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
// 调用数据
userService.deleteUser(userId);
// 校验结果
assertNull(userMapper.selectById(userId));
// 校验调用次数
verify(permissionService, times(1)).processUserDeleted(eq(userId));
}
@Test
public void testGetUserPage() {
// mock 数据
AdminUserDO dbUser = initGetUserPageData();
// 准备参数
UserPageReqVO reqVO = new UserPageReqVO();
reqVO.setUsername("tu");
reqVO.setMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginTime(buildTime(2020, 12, 1));
reqVO.setEndTime(buildTime(2020, 12, 24));
reqVO.setDeptId(1L); // 其中1L 是 2L 的父部门
// mock 方法
List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L)));
when(deptService.getDeptsByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList);
// 调用
PageResult<AdminUserDO> pageResult = userService.getUserPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbUser, pageResult.getList().get(0));
}
@Test
public void testGetUsers() {
// mock 数据
AdminUserDO dbUser = initGetUserPageData();
// 准备参数
UserExportReqVO reqVO = new UserExportReqVO();
reqVO.setUsername("tu");
reqVO.setMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setBeginTime(buildTime(2020, 12, 1));
reqVO.setEndTime(buildTime(2020, 12, 24));
reqVO.setDeptId(1L); // 其中1L 是 2L 的父部门
// mock 方法
List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L)));
when(deptService.getDeptsByParentIdFromCache(eq(reqVO.getDeptId()), eq(true))).thenReturn(deptList);
// 调用
List<AdminUserDO> list = userService.getUsers(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbUser, list.get(0));
}
/**
* 初始化 getUserPage 方法的测试数据
*/
private AdminUserDO initGetUserPageData() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> { // 等会查询到
o.setUsername("tudou");
o.setMobile("15601691300");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
o.setDeptId(2L);
});
userMapper.insert(dbUser);
// 测试 username 不匹配
userMapper.insert(ObjectUtils.cloneIgnoreId(dbUser, o -> o.setUsername("dou")));
// 测试 mobile 不匹配
userMapper.insert(ObjectUtils.cloneIgnoreId(dbUser, o -> o.setMobile("18818260888")));
// 测试 status 不匹配
userMapper.insert(ObjectUtils.cloneIgnoreId(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
userMapper.insert(ObjectUtils.cloneIgnoreId(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 测试 dept 不匹配
userMapper.insert(ObjectUtils.cloneIgnoreId(dbUser, o -> o.setDeptId(0L)));
return dbUser;
}
/**
* 情况一,校验不通过,导致插入失败
*/
@Test
public void testImportUsers_01() {
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
});
// mock 方法,模拟失败
doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validDepts(any());
// 调用
UserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况二,不存在,进行插入
*/
@Test
public void testImportUsers_02() {
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java");
// 调用
UserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true);
// 断言
assertEquals(1, respVO.getCreateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals("java", user.getPassword());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(0, respVO.getFailureUsernames().size());
}
/**
* 情况三,存在,但是不强制更新
*/
@Test
public void testImportUsers_03() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
UserImportRespVO respVO = userService.importUsers(newArrayList(importUser), false);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况四,存在,强制更新
*/
@Test
public void testImportUsers_04() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
UserImportRespVO respVO = userService.importUsers(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(1, respVO.getUpdateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals(0, respVO.getFailureUsernames().size());
}
@Test
public void testCheckUserExists_notExists() {
assertServiceException(() -> userService.checkUserExists(randomLongId()), USER_NOT_EXISTS);
}
@Test
public void testCheckUsernameUnique_usernameExistsForCreate() {
// 准备参数
String username = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setUsername(username)));
// 调用,校验异常
assertServiceException(() -> userService.checkUsernameUnique(null, username),
USER_USERNAME_EXISTS);
}
@Test
public void testCheckUsernameUnique_usernameExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String username = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setUsername(username)));
// 调用,校验异常
assertServiceException(() -> userService.checkUsernameUnique(id, username),
USER_USERNAME_EXISTS);
}
@Test
public void testCheckEmailUnique_emailExistsForCreate() {
// 准备参数
String email = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setEmail(email)));
// 调用,校验异常
assertServiceException(() -> userService.checkEmailUnique(null, email),
USER_EMAIL_EXISTS);
}
@Test
public void testCheckEmailUnique_emailExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String email = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setEmail(email)));
// 调用,校验异常
assertServiceException(() -> userService.checkEmailUnique(id, email),
USER_EMAIL_EXISTS);
}
@Test
public void testCheckMobileUnique_mobileExistsForCreate() {
// 准备参数
String mobile = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile)));
// 调用,校验异常
assertServiceException(() -> userService.checkMobileUnique(null, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testCheckMobileUnique_mobileExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String mobile = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile)));
// 调用,校验异常
assertServiceException(() -> userService.checkMobileUnique(id, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testCheckOldPassword_notExists() {
assertServiceException(() -> userService.checkOldPassword(randomLongId(), randomString()),
USER_NOT_EXISTS);
}
@Test
public void testCheckOldPassword_passwordFailed() {
// mock 数据
AdminUserDO user = randomAdminUserDO();
userMapper.insert(user);
// 准备参数
Long id = user.getId();
String oldPassword = user.getPassword();
// 调用,校验异常
assertServiceException(() -> userService.checkOldPassword(id, oldPassword),
USER_PASSWORD_FAILED);
// 校验调用
verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword()));
}
// @Test //TODO jason 已经移到userCoreService.getUsersByPostIds
// public void testUsersByPostIds() {
// // 准备参数
// Collection<Long> postIds = asSet(10L, 20L);
// // mock 方法
// AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L)));
// userMapper.insert(user1);
// AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L)));
// userMapper.insert(user2);
//
// // 调用
// List<AdminUserDO> result = userService.getUsersByPostIds(postIds);
// // 断言
// assertEquals(1, result.size());
// assertEquals(user1, result.get(0));
// }
// ========== 随机对象 ==========
@SafeVarargs
private static AdminUserDO randomAdminUserDO(Consumer<AdminUserDO>... consumers) {
Consumer<AdminUserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
};
return randomPojo(AdminUserDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,55 @@
spring:
main:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
datasource:
name: ruoyi-vue-pro
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
druid:
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
initial-size: 1 # 单元测试,配置为 1提升启动速度
sql:
init:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
--- #################### 定时任务相关配置 ####################
--- #################### 配置中心相关配置 ####################
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项(单元测试,禁用 Lock4j
# Resilience4j 配置项
--- #################### 监控相关配置 ####################
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
info:
base-package: cn.iocoder.yudao.module
captcha:
timeout: 5m
width: 160
height: 60
enable: true

View File

@ -0,0 +1,4 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
</configuration>

View File

@ -0,0 +1,22 @@
DELETE FROM "system_dept";
DELETE FROM "system_dict_data";
DELETE FROM "system_role";
DELETE FROM "system_role_menu";
DELETE FROM "system_menu";
DELETE FROM "system_user_role";
DELETE FROM "system_user_post";
DELETE FROM "system_dict_type";
DELETE FROM "system_user_session";
DELETE FROM "system_post";
DELETE FROM "system_login_log";
DELETE FROM "system_operate_log";
DELETE FROM "system_user";
DELETE FROM "system_sms_channel";
DELETE FROM "system_sms_template";
DELETE FROM "system_sms_log";
DELETE FROM "system_error_code";
DELETE FROM "system_social_user";
DELETE FROM "system_social_user_bind";
DELETE FROM "system_tenant";
DELETE FROM "system_tenant_package";
DELETE FROM "system_sensitive_word";

View File

@ -0,0 +1,471 @@
CREATE TABLE IF NOT EXISTS "system_dept" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(30) NOT NULL DEFAULT '',
"parent_id" bigint NOT NULL DEFAULT '0',
"sort" int NOT NULL DEFAULT '0',
"leader_user_id" bigint DEFAULT NULL,
"phone" varchar(11) DEFAULT NULL,
"email" varchar(50) DEFAULT NULL,
"status" tinyint NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '部门表';
CREATE TABLE IF NOT EXISTS "system_dict_data" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"sort" int NOT NULL DEFAULT '0',
"label" varchar(100) NOT NULL DEFAULT '',
"value" varchar(100) NOT NULL DEFAULT '',
"dict_type" varchar(100) NOT NULL DEFAULT '',
"status" tinyint NOT NULL DEFAULT '0',
"color_type" varchar(100) NOT NULL DEFAULT '',
"css_class" varchar(100) NOT NULL DEFAULT '',
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '字典数据表';
CREATE TABLE IF NOT EXISTS "system_role" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(30) NOT NULL,
"code" varchar(100) NOT NULL,
"sort" int NOT NULL,
"data_scope" tinyint NOT NULL DEFAULT '1',
"data_scope_dept_ids" varchar(500) NOT NULL DEFAULT '',
"status" tinyint NOT NULL,
"type" tinyint NOT NULL,
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '角色信息表';
CREATE TABLE IF NOT EXISTS "system_role_menu" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"role_id" bigint NOT NULL,
"menu_id" bigint NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '角色和菜单关联表';
CREATE TABLE IF NOT EXISTS "system_menu" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(50) NOT NULL,
"permission" varchar(100) NOT NULL DEFAULT '',
"menu_type" tinyint NOT NULL,
"sort" int NOT NULL DEFAULT '0',
"parent_id" bigint NOT NULL DEFAULT '0',
"path" varchar(200) DEFAULT '',
"icon" varchar(100) DEFAULT '#',
"component" varchar(255) DEFAULT NULL,
"status" tinyint NOT NULL DEFAULT '0',
"visible" bit NOT NULL DEFAULT TRUE,
"hidden" bit NOT NULL DEFAULT TRUE,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '菜单权限表';
CREATE TABLE IF NOT EXISTS "system_user_role" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint NOT NULL,
"role_id" bigint NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp DEFAULT NULL,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp DEFAULT NULL,
"deleted" bit DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '用户和角色关联表';
CREATE TABLE IF NOT EXISTS "system_dict_type" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(100) NOT NULL DEFAULT '',
"type" varchar(100) NOT NULL DEFAULT '',
"status" tinyint NOT NULL DEFAULT '0',
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '字典类型表';
CREATE TABLE IF NOT EXISTS `system_user_session` (
`id` varchar(32) NOT NULL,
`user_id` bigint DEFAULT NULL,
"user_type" tinyint NOT NULL,
`username` varchar(50) NOT NULL DEFAULT '',
`user_ip` varchar(50) DEFAULT NULL,
`user_agent` varchar(512) DEFAULT NULL,
`session_timeout` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updater` varchar(64) DEFAULT '' ,
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY (`id`)
) COMMENT '用户在线 Session';
CREATE TABLE IF NOT EXISTS "system_post" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"code" varchar(64) NOT NULL,
"name" varchar(50) NOT NULL,
"sort" integer NOT NULL,
"status" tinyint NOT NULL,
"remark" varchar(500) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '岗位信息表';
CREATE TABLE IF NOT EXISTS `system_user_post`(
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint DEFAULT NULL,
"post_id" bigint DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY (`id`)
) COMMENT ='用户岗位表';
CREATE TABLE IF NOT EXISTS "system_notice" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"title" varchar(50) NOT NULL COMMENT '公告标题',
"content" text NOT NULL COMMENT '公告内容',
"notice_type" tinyint NOT NULL COMMENT '公告类型1通知 2公告',
"status" tinyint NOT NULL DEFAULT '0' COMMENT '公告状态0正常 1关闭',
"creator" varchar(64) DEFAULT '' COMMENT '创建者',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"updater" varchar(64) DEFAULT '' COMMENT '更新者',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"deleted" bit NOT NULL DEFAULT 0 COMMENT '是否删除',
"tenant_id" bigint not null default '0',
PRIMARY KEY("id")
) COMMENT '通知公告表';
CREATE TABLE IF NOT EXISTS `system_login_log` (
`id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
`log_type` bigint(4) NOT NULL,
"user_id" bigint not null default '0',
"user_type" tinyint NOT NULL,
`trace_id` varchar(64) NOT NULL DEFAULT '',
`username` varchar(50) NOT NULL DEFAULT '',
`result` tinyint(4) NOT NULL,
`user_ip` varchar(50) NOT NULL,
`user_agent` varchar(512) NOT NULL,
`creator` varchar(64) DEFAULT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updater` varchar(64) DEFAULT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bit(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) COMMENT ='系统访问记录';
CREATE TABLE IF NOT EXISTS `system_operate_log` (
`id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
`trace_id` varchar(64) NOT NULL DEFAULT '',
`user_id` bigint(20) NOT NULL,
"user_type" tinyint not null default '0',
`module` varchar(50) NOT NULL,
`name` varchar(50) NOT NULL,
`operate_type` bigint(4) NOT NULL DEFAULT '0',
`content` varchar(2000) NOT NULL DEFAULT '',
`exts` varchar(512) NOT NULL DEFAULT '',
`request_method` varchar(16) DEFAULT '',
`request_url` varchar(255) DEFAULT '',
`user_ip` varchar(50) DEFAULT NULL,
`user_agent` varchar(200) DEFAULT NULL,
`java_method` varchar(512) NOT NULL DEFAULT '',
`java_method_args` varchar(8000) DEFAULT '',
`start_time` datetime NOT NULL,
`duration` int(11) NOT NULL,
`result_code` int(11) NOT NULL DEFAULT '0',
`result_msg` varchar(512) DEFAULT '',
`result_data` varchar(4000) DEFAULT '',
`creator` varchar(64) DEFAULT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updater` varchar(64) DEFAULT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bit(1) NOT NULL DEFAULT '0',
"tenant_id" bigint not null default '0',
PRIMARY KEY (`id`)
) COMMENT ='操作日志记录';
CREATE TABLE IF NOT EXISTS "system_user" (
"id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
"username" varchar(30) not null,
"password" varchar(100) not null default '',
"nickname" varchar(30) not null,
"remark" varchar(500) default null,
"dept_id" bigint default null,
"post_ids" varchar(255) default null,
"email" varchar(50) default '',
"mobile" varchar(11) default '',
"sex" tinyint default '0',
"avatar" varchar(100) default '',
"status" tinyint not null default '0',
"login_ip" varchar(50) default '',
"login_date" timestamp default null,
"creator" varchar(64) default '',
"create_time" timestamp not null default current_timestamp,
"updater" varchar(64) default '',
"update_time" timestamp not null default current_timestamp,
"deleted" bit not null default false,
"tenant_id" bigint not null default '0',
primary key ("id")
) comment '用户信息表';
CREATE TABLE IF NOT EXISTS "inf_api_access_log" (
"id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
"trace_id" varchar(64) not null default '',
"user_id" bigint not null default '0',
"user_type" tinyint not null default '0',
"application_name" varchar(50) not null,
"request_method" varchar(16) not null default '',
"request_url" varchar(255) not null default '',
"request_params" varchar(8000) not null default '',
"user_ip" varchar(50) not null,
"user_agent" varchar(512) not null,
"begin_time" timestamp not null,
"end_time" timestamp not null,
"duration" integer not null,
"result_code" integer not null default '0',
"result_msg" varchar(512) default '',
"creator" varchar(64) default '',
"create_time" timestamp not null default current_timestamp,
"updater" varchar(64) default '',
"update_time" timestamp not null default current_timestamp,
"deleted" bit not null default false,
"tenant_id" bigint not null default '0',
primary key ("id")
) COMMENT 'API 访问日志表';
CREATE TABLE IF NOT EXISTS "inf_api_error_log" (
"id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
"trace_id" varchar(64) not null,
"user_id" bigint not null default '0',
"user_type" tinyint not null default '0',
"application_name" varchar(50) not null,
"request_method" varchar(16) not null,
"request_url" varchar(255) not null,
"request_params" varchar(8000) not null,
"user_ip" varchar(50) not null,
"user_agent" varchar(512) not null,
"exception_time" timestamp not null,
"exception_name" varchar(128) not null default '',
"exception_message" clob not null,
"exception_root_cause_message" clob not null,
"exception_stack_trace" clob not null,
"exception_class_name" varchar(512) not null,
"exception_file_name" varchar(512) not null,
"exception_method_name" varchar(512) not null,
"exception_line_number" integer not null,
"process_status" tinyint not null,
"process_time" timestamp default null,
"process_user_id" bigint default '0',
"creator" varchar(64) default '',
"create_time" timestamp not null default current_timestamp,
"updater" varchar(64) default '',
"update_time" timestamp not null default current_timestamp,
"deleted" bit not null default false,
"tenant_id" bigint not null default '0',
primary key ("id")
) COMMENT '系统异常日志';
CREATE TABLE IF NOT EXISTS "system_sms_channel" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"signature" varchar(10) NOT NULL,
"code" varchar(63) NOT NULL,
"status" tinyint NOT NULL,
"remark" varchar(255) DEFAULT NULL,
"api_key" varchar(63) NOT NULL,
"api_secret" varchar(63) DEFAULT NULL,
"callback_url" varchar(255) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '短信渠道';
CREATE TABLE IF NOT EXISTS "system_sms_template" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"type" tinyint NOT NULL,
"status" tinyint NOT NULL,
"code" varchar(63) NOT NULL,
"name" varchar(63) NOT NULL,
"content" varchar(255) NOT NULL,
"params" varchar(255) NOT NULL,
"remark" varchar(255) DEFAULT NULL,
"api_template_id" varchar(63) NOT NULL,
"channel_id" bigint NOT NULL,
"channel_code" varchar(63) NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '短信模板';
CREATE TABLE IF NOT EXISTS "system_sms_log" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"channel_id" bigint NOT NULL,
"channel_code" varchar(63) NOT NULL,
"template_id" bigint NOT NULL,
"template_code" varchar(63) NOT NULL,
"template_type" tinyint NOT NULL,
"template_content" varchar(255) NOT NULL,
"template_params" varchar(255) NOT NULL,
"api_template_id" varchar(63) NOT NULL,
"mobile" varchar(11) NOT NULL,
"user_id" bigint DEFAULT '0',
"user_type" tinyint DEFAULT '0',
"send_status" tinyint NOT NULL DEFAULT '0',
"send_time" timestamp DEFAULT NULL,
"send_code" int DEFAULT NULL,
"send_msg" varchar(255) DEFAULT NULL,
"api_send_code" varchar(63) DEFAULT NULL,
"api_send_msg" varchar(255) DEFAULT NULL,
"api_request_id" varchar(255) DEFAULT NULL,
"api_serial_no" varchar(255) DEFAULT NULL,
"receive_status" tinyint NOT NULL DEFAULT '0',
"receive_time" timestamp DEFAULT NULL,
"api_receive_code" varchar(63) DEFAULT NULL,
"api_receive_msg" varchar(255) DEFAULT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '短信日志';
CREATE TABLE IF NOT EXISTS "system_error_code" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"type" tinyint NOT NULL DEFAULT '0',
"application_name" varchar(50) NOT NULL,
"code" int NOT NULL DEFAULT '0',
"message" varchar(512) NOT NULL DEFAULT '',
"memo" varchar(512) DEFAULT '',
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '错误码表';
CREATE TABLE IF NOT EXISTS "system_social_user" (
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"type" tinyint NOT NULL,
"openid" varchar(64) NOT NULL,
"token" varchar(256) DEFAULT NULL,
"raw_token_info" varchar(1024) NOT NULL,
"nickname" varchar(32) NOT NULL,
"avatar" varchar(255) DEFAULT NULL,
"raw_user_info" varchar(1024) NOT NULL,
"code" varchar(64) NOT NULL,
"state" varchar(64),
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '社交用户';
CREATE TABLE IF NOT EXISTS "system_social_user_bind" (
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint NOT NULL,
"user_type" tinyint NOT NULL,
"social_type" tinyint NOT NULL,
"social_user_id" number NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '社交用户的绑定';
CREATE TABLE IF NOT EXISTS "system_tenant" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(63) NOT NULL,
"contact_user_id" bigint NOT NULL DEFAULT '0',
"contact_name" varchar(255) NOT NULL,
"contact_mobile" varchar(255),
"status" tinyint NOT NULL,
"domain" varchar(63) DEFAULT '',
"package_id" bigint NOT NULL,
"expire_time" timestamp NOT NULL,
"account_count" int NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '租户';
CREATE TABLE IF NOT EXISTS "system_tenant_package" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(30) NOT NULL,
"status" tinyint NOT NULL,
"remark" varchar(256),
"menu_ids" varchar(2048) NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '租户套餐表';
CREATE TABLE IF NOT EXISTS "system_sensitive_word" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(255) NOT NULL,
"tags" varchar(1024) NOT NULL,
"status" bit NOT NULL DEFAULT FALSE,
"description" varchar(512),
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '系统敏感词';