1、线程池bean命名规范;2、菜单管理controller、service单元测试完善;3、已有controller单测替换mock static SecurityFrameworkUtils;

This commit is contained in:
gaoqr
2026-01-26 17:42:31 +08:00
parent b09892a349
commit 0d4700e225
12 changed files with 1008 additions and 279 deletions
@@ -23,4 +23,8 @@ public @interface WithMockLoginUser {
boolean isSuperAdmin() default false; boolean isSuperAdmin() default false;
long organId() default 0L; long organId() default 0L;
int userType() default 1;
long productId() default 0L;
} }
@@ -12,9 +12,9 @@ import java.util.concurrent.ThreadPoolExecutor;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public class ExecutorThreadPoolConfiguration { public class ExecutorThreadPoolConfiguration {
public static final String EXECUTOR_IMPOT_THREAD_POOL_TASK_EXECUTOR = "EXECUTOR_IMPORT_THREAD_POOL_TASK_EXECUTOR"; public static final String EXECUTOR_IMPOT_THREAD_POOL_TASK_EXECUTOR = "executorImportThreadPoolTaskExecutor";
@Bean(EXECUTOR_IMPOT_THREAD_POOL_TASK_EXECUTOR) @Bean("executorImportThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor notifyThreadPoolTaskExecutor() { public ThreadPoolTaskExecutor notifyThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8); // 设置核心线程数 executor.setCorePoolSize(8); // 设置核心线程数
@@ -0,0 +1,32 @@
package com.cf.imes.module.system.framework.executor.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* system服务线程池配置
*
* @author Gqr
* @since 2026/1/26 14:29
*/
@Configuration(proxyBeanMethods = false)
public class SystemThreadPoolConfiguration {
public static final String SYSTEM_PERMISSION_THREAD_POOL_TASK_EXECUTOR = "systemPermissionThreadPoolTaskExecutor";
@Bean("systemPermissionThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor systemPermissionThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8); // 设置核心线程数
executor.setMaxPoolSize(8); // 设置最大线程数
executor.setKeepAliveSeconds(60); // 设置空闲时间
executor.setQueueCapacity(100); // 设置队列大小
executor.setThreadNamePrefix("system-permission-Executor-"); // 配置线程池的前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 进行加载
executor.initialize();
return executor;
}
}
@@ -36,6 +36,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.ScanOptions;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -53,6 +54,7 @@ import java.util.stream.IntStream;
import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertList; import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertList;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static com.cf.imes.module.system.framework.executor.config.SystemThreadPoolConfiguration.SYSTEM_PERMISSION_THREAD_POOL_TASK_EXECUTOR;
/** /**
* 菜单 Service 实现 * 菜单 Service 实现
@@ -80,6 +82,8 @@ public class MenuServiceImpl implements MenuService {
@Resource @Resource
private TransProperties transProperties; private TransProperties transProperties;
@Resource(name = SYSTEM_PERMISSION_THREAD_POOL_TASK_EXECUTOR)
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
private static final String FIELD_CHARACTER = ":*:%s"; private static final String FIELD_CHARACTER = ":*:%s";
@@ -167,7 +171,7 @@ public class MenuServiceImpl implements MenuService {
* @param sourceType * @param sourceType
*/ */
private void validateManageSourceTypeOperatePermission(Integer sourceType) { private void validateManageSourceTypeOperatePermission(Integer sourceType) {
if (!SecurityFrameworkUtils.isCfOrg() && sourceType.equals(MenuSourceTypeEnum.MANAGEMENT.getType())) { if (!SecurityFrameworkUtils.isCfOrg() && MenuSourceTypeEnum.MANAGEMENT.getType().equals(sourceType)) {
throw new ServiceException(MANAGEMENT_MENU_OPERATION_PERMISSION_ERROR); throw new ServiceException(MANAGEMENT_MENU_OPERATION_PERMISSION_ERROR);
} }
} }
@@ -181,33 +185,32 @@ public class MenuServiceImpl implements MenuService {
*/ */
private void flushCacheWhenOperateMenu(Long menuId, String newPermission, String oldPermission) { private void flushCacheWhenOperateMenu(Long menuId, String newPermission, String oldPermission) {
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
if (ObjectUtil.isNotNull(oldPermission)) { try {
// 移除 USER_ROLE_ID_LIST:*:旧的permission if (ObjectUtil.isNotNull(oldPermission)) {
String oldMenuPerPattern = String.format(String.valueOf(new StringBuffer(RedisKeyConstants.PERMISSION_MENU_ID_LIST).append(FIELD_CHARACTER)), oldPermission); // 移除 USER_ROLE_ID_LIST:*:旧的permission
Long oldBatchDelPerNum = scanAndDelKeys(oldMenuPerPattern); String oldMenuPerPattern = String.format(String.valueOf(new StringBuffer(RedisKeyConstants.PERMISSION_MENU_ID_LIST).append(FIELD_CHARACTER)), oldPermission);
log.info("[updateMenu] 批量删除redis[{}]数量:{}", oldMenuPerPattern, oldBatchDelPerNum); Long oldBatchDelPerNum = scanAndDelKeys(oldMenuPerPattern);
} log.info("[updateMenu] 批量删除redis[{}]数量:{}", oldMenuPerPattern, oldBatchDelPerNum);
}
if (ObjectUtil.isNotNull(newPermission)) { if (ObjectUtil.isNotNull(newPermission)) {
// 移除 USER_ROLE_ID_LIST:*:新的permission // 移除 USER_ROLE_ID_LIST:*:新的permission
String newMenuPerPattern = String.format(String.valueOf(new StringBuffer(RedisKeyConstants.PERMISSION_MENU_ID_LIST).append(FIELD_CHARACTER)), newPermission); String newMenuPerPattern = String.format(String.valueOf(new StringBuffer(RedisKeyConstants.PERMISSION_MENU_ID_LIST).append(FIELD_CHARACTER)), newPermission);
Long newBatchDelPerNum = scanAndDelKeys(newMenuPerPattern); Long newBatchDelPerNum = scanAndDelKeys(newMenuPerPattern);
log.info("[updateMenu] 批量删除redis[{}]数量:{}", newMenuPerPattern, newBatchDelPerNum); log.info("[updateMenu] 批量删除redis[{}]数量:{}", newMenuPerPattern, newBatchDelPerNum);
} }
if (ObjectUtil.isNotNull(menuId)) {
// 移除 MENU_ROLE_ID_LIST:*:menuId // 移除 MENU_ROLE_ID_LIST:*:menuId
String menuRolePattern = String.format(String.valueOf(new StringBuffer(RedisKeyConstants.MENU_ROLE_ID_LIST).append(FIELD_CHARACTER)), menuId); String menuRolePattern = String.format(String.valueOf(new StringBuffer(RedisKeyConstants.MENU_ROLE_ID_LIST).append(FIELD_CHARACTER)), menuId);
Long batchDelRoleNum = scanAndDelKeys(menuRolePattern); Long batchDelRoleNum = scanAndDelKeys(menuRolePattern);
log.info("[deleteMenu] 批量删除redis[{}]数量:{}", menuRolePattern, batchDelRoleNum); log.info("[deleteMenu] 批量删除redis[{}]数量:{}", menuRolePattern, batchDelRoleNum);
}
// 清空本地缓存 // 清空本地缓存
redisTemplate.convertAndSend(com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, ""); redisTemplate.convertAndSend(com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, "");
}).exceptionally(e -> { } catch (Exception e) {
log.error("[deleteMenu] redis 清空PERMISSION_MENU_ID_LIST或message发送失败, topic:{}, 异常:{}", com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, e); log.error("[deleteMenu] redis 清空PERMISSION_MENU_ID_LIST或message发送失败, topic:{}, 异常:{}", com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, e);
return null; }
}); }, threadPoolTaskExecutor);
} }
@@ -263,7 +266,7 @@ public class MenuServiceImpl implements MenuService {
@Override @Override
public List<MenuDO> getMenuList(Collection<Long> ids) { public List<MenuDO> getMenuList(Collection<Long> ids) {
return menuMapper.selectBatchIds(ids); return menuMapper.selectByIds(ids);
} }
@Override @Override
@@ -272,7 +275,7 @@ public class MenuServiceImpl implements MenuService {
if(CollUtil.isEmpty(ids)) { if(CollUtil.isEmpty(ids)) {
return new ArrayList<>(); return new ArrayList<>();
} }
return menuMapper.selectBatchIds(ids); return menuMapper.selectByIds(ids);
} }
/** /**
@@ -300,8 +303,7 @@ public class MenuServiceImpl implements MenuService {
throw new ServiceException(MENU_PARENT_NOT_EXISTS); throw new ServiceException(MENU_PARENT_NOT_EXISTS);
} }
// 父菜单必须是目录或者菜单类型 // 父菜单必须是目录或者菜单类型
if (!MenuTypeEnum.DIR.getType().equals(menu.getType()) if (MenuTypeEnum.BUTTON.getType().equals(menu.getType())) {
&& !MenuTypeEnum.MENU.getType().equals(menu.getType())) {
throw new ServiceException(MENU_PARENT_NOT_DIR_OR_MENU); throw new ServiceException(MENU_PARENT_NOT_DIR_OR_MENU);
} }
} }
@@ -407,7 +409,7 @@ public class MenuServiceImpl implements MenuService {
String src = item.getString("src"); String src = item.getString("src");
String dst = item.getString("dst"); String dst = item.getString("dst");
if (menuDO == null || !menuDO.getName().equals(UnicodeUtil.toString(src))) { if (!menuDO.getName().equals(UnicodeUtil.toString(src))) {
continue; continue;
} }
@@ -2,6 +2,7 @@ package com.cf.imes.module.system.common;
import com.cf.imes.framework.security.core.LoginUser; import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.test.WithMockLoginUser; import com.cf.imes.framework.security.test.WithMockLoginUser;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
@@ -16,6 +17,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
* @author Gqr * @author Gqr
* @since 2026/1/16 10:20 * @since 2026/1/16 10:20
*/ */
@Slf4j
public class WithMockLoginUserExtension implements BeforeEachCallback, AfterEachCallback { public class WithMockLoginUserExtension implements BeforeEachCallback, AfterEachCallback {
@Override @Override
public void afterEach(ExtensionContext context) { public void afterEach(ExtensionContext context) {
@@ -33,6 +35,8 @@ public class WithMockLoginUserExtension implements BeforeEachCallback, AfterEach
loginUser.setDeptId(ann.deptId()); loginUser.setDeptId(ann.deptId());
loginUser.setNickname(ann.username()); loginUser.setNickname(ann.username());
loginUser.setIsSupAdmin(ann.isSuperAdmin()); loginUser.setIsSupAdmin(ann.isSuperAdmin());
loginUser.setUserType(ann.userType());
loginUser.setProductId(ann.productId());
} else { } else {
return; return;
} }
@@ -6,10 +6,12 @@ import com.cf.imes.framework.ip.core.service.dto.IPQueryDataRespDTO;
import com.cf.imes.framework.security.config.SecurityProperties; import com.cf.imes.framework.security.config.SecurityProperties;
import com.cf.imes.framework.security.core.LoginUser; import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest; import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.framework.web.config.ChenfengWebAutoConfiguration; import com.cf.imes.framework.web.config.ChenfengWebAutoConfiguration;
import com.cf.imes.module.infra.api.logger.ApiErrorLogApi; import com.cf.imes.module.infra.api.logger.ApiErrorLogApi;
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import com.cf.imes.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginReqVO; import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginReqVO;
import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginRespVO; import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginRespVO;
import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginSmsCheckReqVO; import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginSmsCheckReqVO;
@@ -31,6 +33,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic; import org.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -74,6 +77,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@WebMvcTest(controllers = AuthController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class) @WebMvcTest(controllers = AuthController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
@Import(ChenfengWebAutoConfiguration.class) @Import(ChenfengWebAutoConfiguration.class)
@ExtendWith(WithMockLoginUserExtension.class)
@TestPropertySource(properties = { @TestPropertySource(properties = {
"spring.application.name=test-app" "spring.application.name=test-app"
}) })
@@ -283,136 +287,138 @@ class AuthControllerTest extends BaseWebUnitTest {
} }
@Test @Test
@WithMockLoginUser(userId = 1L, organId = 100L)
void testGetPermissionInfo_success() throws Exception { void testGetPermissionInfo_success() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> utilities = mockStatic(SecurityFrameworkUtils.class)) { AdminUserDO user = new AdminUserDO();
utilities.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); user.setId(1L);
utilities.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(false); user.setOrganId(100L);
when(userService.getUser(1L)).thenReturn(user);
AdminUserDO user = new AdminUserDO(); OrganizationDO organization = new OrganizationDO();
user.setId(1L); when(organService.validOrgan(100L)).thenReturn(organization);
user.setOrganId(100L);
when(userService.getUser(1L)).thenReturn(user);
OrganizationDO organization = new OrganizationDO(); when(ipQueryService.serviceEnable()).thenReturn(false);
when(organService.validOrgan(100L)).thenReturn(organization);
when(ipQueryService.serviceEnable()).thenReturn(false); Set<Long> roleIds = new HashSet<>(Arrays.asList(10L, 20L));
when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds);
Set<Long> roleIds = new HashSet<>(Arrays.asList(10L, 20L)); RoleDO role1 = new RoleDO();
when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds); role1.setId(10L);
role1.setStatus(1);
RoleDO role2 = new RoleDO();
role2.setId(20L);
role2.setStatus(1);
when(roleService.getRoleList1(roleIds))
.thenReturn(new ArrayList<>(Arrays.asList(role1, role2)));
RoleDO role1 = new RoleDO(); role1.setId(10L); role1.setStatus(1); Set<Long> menuIds = new HashSet<>(Arrays.asList(1000L, 2000L));
RoleDO role2 = new RoleDO(); role2.setId(20L); role2.setStatus(1); when(permissionService.getRoleMenuListByRoleId2(anySet(), eq(100L))).thenReturn(menuIds);
when(roleService.getRoleList1(roleIds))
.thenReturn(new ArrayList<>(Arrays.asList(role1, role2)));
Set<Long> menuIds = new HashSet<>(Arrays.asList(1000L, 2000L)); MenuDO menu1 = new MenuDO();
when(permissionService.getRoleMenuListByRoleId2(anySet(), eq(100L))).thenReturn(menuIds); menu1.setId(1000L);
menu1.setStatus(1);
MenuDO menu2 = new MenuDO();
menu2.setId(2000L);
menu2.setStatus(1);
when(menuService.getCustomEndPointMenuList(menuIds))
.thenReturn(new ArrayList<>(Arrays.asList(menu1, menu2)));
MenuDO menu1 = new MenuDO(); menu1.setId(1000L); menu1.setStatus(1); mockMvc.perform(get("/admin-api/system/auth/get-permission-info"))
MenuDO menu2 = new MenuDO(); menu2.setId(2000L); menu2.setStatus(1); .andExpect(status().isOk())
when(menuService.getCustomEndPointMenuList(menuIds)) .andExpect(jsonPath("$.data").exists());
.thenReturn(new ArrayList<>(Arrays.asList(menu1, menu2)));
mockMvc.perform(get("/admin-api/system/auth/get-permission-info")) // 验证调用顺序/次数(可选)
.andExpect(status().isOk()) verify(userService, times(1)).getUser(1L);
.andExpect(jsonPath("$.data").exists()); verify(organService, times(1)).validOrgan(100L);
verify(roleService, times(1)).getRoleList1(roleIds);
// 验证调用顺序/次数(可选) verify(menuService, times(1)).getCustomEndPointMenuList(menuIds);
verify(userService, times(1)).getUser(1L);
verify(organService, times(1)).validOrgan(100L);
verify(roleService, times(1)).getRoleList1(roleIds);
verify(menuService, times(1)).getCustomEndPointMenuList(menuIds);
}
} }
@Test @Test
@WithMockLoginUser(userId = 1L, organId = 100L, userType = 2)
void testGetPermissionInfo_success_branch() throws Exception { void testGetPermissionInfo_success_branch() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> utilities = mockStatic(SecurityFrameworkUtils.class)) { AdminUserDO user = new AdminUserDO();
utilities.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); user.setId(1L);
utilities.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(true); user.setOrganId(100L);
when(userService.getUser(1L)).thenReturn(user);
AdminUserDO user = new AdminUserDO(); OrganizationDO organization = new OrganizationDO();
user.setId(1L); when(organService.validOrgan(100L)).thenReturn(organization);
user.setOrganId(100L);
when(userService.getUser(1L)).thenReturn(user);
OrganizationDO organization = new OrganizationDO(); IPQueryDataRespDTO ipResp = new IPQueryDataRespDTO();
when(organService.validOrgan(100L)).thenReturn(organization); ipResp.setProv("浙江");
ipResp.setCity("杭州");
ipResp.setArea("西湖");
when(ipQueryService.serviceEnable()).thenReturn(true);
when(ipQueryService.querySource(anyString())).thenReturn(ipResp);
IPQueryDataRespDTO ipResp = new IPQueryDataRespDTO(); Set<Long> roleIds = new HashSet<>(Arrays.asList(10L, 20L));
ipResp.setProv("浙江"); when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds);
ipResp.setCity("杭州");
ipResp.setArea("西湖");
when(ipQueryService.serviceEnable()).thenReturn(true);
when(ipQueryService.querySource(anyString())).thenReturn(ipResp);
Set<Long> roleIds = new HashSet<>(Arrays.asList(10L, 20L)); RoleDO role1 = new RoleDO();
when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds); role1.setId(10L);
role1.setStatus(1);
RoleDO role2 = new RoleDO();
role2.setId(20L);
role2.setStatus(1);
when(roleService.getRoleList1(roleIds))
.thenReturn(new ArrayList<>(Arrays.asList(role1, role2)));
RoleDO role1 = new RoleDO(); role1.setId(10L); role1.setStatus(1); Set<Long> menuIds = new HashSet<>(Arrays.asList(1000L, 2000L));
RoleDO role2 = new RoleDO(); role2.setId(20L); role2.setStatus(1); when(permissionService.getRoleMenuListByRoleId2(anySet(), eq(100L))).thenReturn(menuIds);
when(roleService.getRoleList1(roleIds))
.thenReturn(new ArrayList<>(Arrays.asList(role1, role2)));
Set<Long> menuIds = new HashSet<>(Arrays.asList(1000L, 2000L)); MenuDO menu1 = new MenuDO();
when(permissionService.getRoleMenuListByRoleId2(anySet(), eq(100L))).thenReturn(menuIds); menu1.setId(1000L);
menu1.setStatus(1);
MenuDO menu2 = new MenuDO();
menu2.setId(2000L);
menu2.setStatus(1);
when(menuService.getManageEndPointMenuList(menuIds))
.thenReturn(new ArrayList<>(Arrays.asList(menu1, menu2)));
MenuDO menu1 = new MenuDO(); menu1.setId(1000L); menu1.setStatus(1); mockMvc.perform(get("/admin-api/system/auth/get-permission-info"))
MenuDO menu2 = new MenuDO(); menu2.setId(2000L); menu2.setStatus(1); .andExpect(status().isOk())
when(menuService.getManageEndPointMenuList(menuIds)) .andExpect(jsonPath("$.data").exists());
.thenReturn(new ArrayList<>(Arrays.asList(menu1, menu2)));
mockMvc.perform(get("/admin-api/system/auth/get-permission-info")) // 验证调用顺序/次数(可选)
.andExpect(status().isOk()) verify(userService, times(1)).getUser(1L);
.andExpect(jsonPath("$.data").exists()); verify(organService, times(1)).validOrgan(100L);
verify(ipQueryService, times(1)).querySource(anyString());
// 验证调用顺序/次数(可选) verify(roleService, times(1)).getRoleList1(roleIds);
verify(userService, times(1)).getUser(1L); verify(menuService, times(1)).getManageEndPointMenuList(menuIds);
verify(organService, times(1)).validOrgan(100L);
verify(ipQueryService, times(1)).querySource(anyString());
verify(roleService, times(1)).getRoleList1(roleIds);
verify(menuService, times(1)).getManageEndPointMenuList(menuIds);
}
} }
@Test @Test
@WithMockLoginUser(userId = 1L, organId = 100L)
void testGetPermissionInfo_success_roleEmpty() throws Exception { void testGetPermissionInfo_success_roleEmpty() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> utilities = mockStatic(SecurityFrameworkUtils.class)) { AdminUserDO user = new AdminUserDO();
utilities.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); user.setId(1L);
utilities.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(true); user.setOrganId(100L);
when(userService.getUser(1L)).thenReturn(user);
AdminUserDO user = new AdminUserDO(); OrganizationDO organization = new OrganizationDO();
user.setId(1L); when(organService.validOrgan(100L)).thenReturn(organization);
user.setOrganId(100L);
when(userService.getUser(1L)).thenReturn(user);
OrganizationDO organization = new OrganizationDO(); when(ipQueryService.serviceEnable()).thenReturn(false);
when(organService.validOrgan(100L)).thenReturn(organization);
when(ipQueryService.serviceEnable()).thenReturn(false); Set<Long> roleIds = new HashSet<>();
when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds);
Set<Long> roleIds = new HashSet<>(); mockMvc.perform(get("/admin-api/system/auth/get-permission-info"))
when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds); .andExpect(status().isOk())
.andExpect(jsonPath("$.data.roles").isEmpty());
mockMvc.perform(get("/admin-api/system/auth/get-permission-info")) // 验证调用顺序/次数(可选)
.andExpect(status().isOk()) verify(userService, times(1)).getUser(1L);
.andExpect(jsonPath("$.data.roles").isEmpty()); verify(organService, times(1)).validOrgan(100L);
verify(ipQueryService, never()).querySource(anyString());
// 验证调用顺序/次数(可选) verify(roleService, never()).getRoleList1(roleIds);
verify(userService, times(1)).getUser(1L); verify(menuService, never()).getManageEndPointMenuList(anySet());
verify(organService, times(1)).validOrgan(100L);
verify(ipQueryService, never()).querySource(anyString());
verify(roleService, never()).getRoleList1(roleIds);
verify(menuService, never()).getManageEndPointMenuList(anySet());
}
} }
@Test @Test
@WithMockLoginUser(userId = 1L)
void testGetPermissionInfo_userNull() throws Exception { void testGetPermissionInfo_userNull() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> utilities = mockStatic(SecurityFrameworkUtils.class)) {
utilities.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
when(userService.getUser(1L)).thenReturn(null); when(userService.getUser(1L)).thenReturn(null);
mockMvc.perform(get("/admin-api/system/auth/get-permission-info")) mockMvc.perform(get("/admin-api/system/auth/get-permission-info"))
@@ -420,7 +426,6 @@ class AuthControllerTest extends BaseWebUnitTest {
.andExpect(jsonPath("$.data").isEmpty()); // 返回 null .andExpect(jsonPath("$.data").isEmpty()); // 返回 null
verify(userService, times(1)).getUser(1L); verify(userService, times(1)).getUser(1L);
}
} }
@Test @Test
@@ -2,6 +2,7 @@ package com.cf.imes.module.system.controller.admin.dict;
import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO; import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataTransReqVO; import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataTransReqVO;
@@ -37,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @since 2026/1/14 15:38 * @since 2026/1/14 15:38
*/ */
@WebMvcTest(controllers = DictDataController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class) @WebMvcTest(controllers = DictDataController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
class DictDataControllerTest { class DictDataControllerTest extends BaseWebUnitTest {
@Autowired @Autowired
private MockMvc mockMvc; private MockMvc mockMvc;
@@ -3,6 +3,7 @@ package com.cf.imes.module.system.controller.admin.dict;
import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.excel.core.util.ExcelUtils; import com.cf.imes.framework.excel.core.util.ExcelUtils;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.module.system.controller.admin.dict.vo.type.DictTypeRespVO; import com.cf.imes.module.system.controller.admin.dict.vo.type.DictTypeRespVO;
import com.cf.imes.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; import com.cf.imes.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dict.DictTypeDO; import com.cf.imes.module.system.dal.dataobject.dict.DictTypeDO;
@@ -42,7 +43,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @since 2026/1/14 17:25 * @since 2026/1/14 17:25
*/ */
@WebMvcTest(controllers = DictTypeController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class) @WebMvcTest(controllers = DictTypeController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
public class DictTypeControllerTest { class DictTypeControllerTest extends BaseWebUnitTest {
@Autowired @Autowired
private MockMvc mockMvc; private MockMvc mockMvc;
@@ -3,7 +3,9 @@ package com.cf.imes.module.system.controller.admin.funds.products;
import com.cf.imes.framework.common.pojo.PageParam; import com.cf.imes.framework.common.pojo.PageParam;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest; import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO; import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO; import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO; import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
@@ -11,6 +13,7 @@ import com.cf.imes.module.system.enums.products.ProductStatusEnum;
import com.cf.imes.module.system.service.funds.products.ProductsService; import com.cf.imes.module.system.service.funds.products.ProductsService;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
@@ -41,6 +44,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @since 2025/10/14 11:16 * @since 2025/10/14 11:16
*/ */
@WebMvcTest(controllers = ProductsController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class) @WebMvcTest(controllers = ProductsController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
@ExtendWith(WithMockLoginUserExtension.class)
class ProductsControllerTest extends BaseWebUnitTest { class ProductsControllerTest extends BaseWebUnitTest {
@Autowired @Autowired
private MockMvc mockMvc; private MockMvc mockMvc;
@@ -164,6 +168,7 @@ class ProductsControllerTest extends BaseWebUnitTest {
} }
@Test @Test
@WithMockLoginUser(userType = 2)
void testGetAvailableProductsPage() throws Exception { void testGetAvailableProductsPage() throws Exception {
ProductPageReqVO reqVO = new ProductPageReqVO(); ProductPageReqVO reqVO = new ProductPageReqVO();
PageResult<ProductsDO> mockPage = new PageResult<>(); PageResult<ProductsDO> mockPage = new PageResult<>();
@@ -171,21 +176,17 @@ class ProductsControllerTest extends BaseWebUnitTest {
mockPage.setList(List.of(new ProductsDO().setId(1L).setProductName("已上架产品"))); mockPage.setList(List.of(new ProductsDO().setId(1L).setProductName("已上架产品")));
when(productsService.getPage(any())).thenReturn(mockPage); when(productsService.getPage(any())).thenReturn(mockPage);
// mock 管理端标识 mockMvc.perform(get("/system/products/available")
try (var ignored = mockStatic(SecurityFrameworkUtils.class)) { .contentType(MediaType.APPLICATION_JSON)
when(SecurityFrameworkUtils.isManageEndPoint()).thenReturn(true); .content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.list[0].productName").value("已上架产品"));
mockMvc.perform(get("/system/products/available") verify(productsService, times(1)).getPage(any());
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.list[0].productName").value("已上架产品"));
verify(productsService, times(1)).getPage(any());
}
} }
@Test @Test
@WithMockLoginUser(productId = 123L)
void testGetAvailableProductsPage_nonManager() throws Exception { void testGetAvailableProductsPage_nonManager() throws Exception {
ProductPageReqVO reqVO = new ProductPageReqVO(); ProductPageReqVO reqVO = new ProductPageReqVO();
@@ -1,15 +1,15 @@
package com.cf.imes.module.system.controller.admin.funds.purchase; package com.cf.imes.module.system.controller.admin.funds.purchase;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest; import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordPageReqVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordRespVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordRespVO;
import com.cf.imes.module.system.service.funds.purchase.PurchaseService; import com.cf.imes.module.system.service.funds.purchase.PurchaseService;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
@@ -39,6 +39,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
controllers = PurchaseController.class, controllers = PurchaseController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class excludeAutoConfiguration = SecurityAutoConfiguration.class
) )
@ExtendWith(WithMockLoginUserExtension.class)
class PurchaseControllerTest extends BaseWebUnitTest { class PurchaseControllerTest extends BaseWebUnitTest {
@Autowired @Autowired
private MockMvc mockMvc; private MockMvc mockMvc;
@@ -50,13 +51,8 @@ class PurchaseControllerTest extends BaseWebUnitTest {
* 非管理端:获取购买记录分页 * 非管理端:获取购买记录分页
*/ */
@Test @Test
@WithMockLoginUser(organId = 1L)
void testGetPurchaseRecord_nonManage() throws Exception { void testGetPurchaseRecord_nonManage() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> mocked = Mockito.mockStatic(SecurityFrameworkUtils.class)) {
// mock 当前为非管理端
mocked.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(false);
mocked.when(SecurityFrameworkUtils::getUserOrganId).thenReturn(1L);
// mock service 返回 // mock service 返回
PurchaseRecordRespVO respVO = new PurchaseRecordRespVO(); PurchaseRecordRespVO respVO = new PurchaseRecordRespVO();
respVO.setId(1L); respVO.setId(1L);
@@ -84,18 +80,15 @@ class PurchaseControllerTest extends BaseWebUnitTest {
.getPurchaseRecord(captor.capture()); .getPurchaseRecord(captor.capture());
assertEquals(1L, captor.getValue().getOrganId()); assertEquals(1L, captor.getValue().getOrganId());
}
} }
/** /**
* 管理端:获取购买记录分页 * 管理端:获取购买记录分页
*/ */
@Test @Test
@WithMockLoginUser(userType = 2)
void testGetPurchaseRecord_manage() throws Exception { void testGetPurchaseRecord_manage() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> mocked = Mockito.mockStatic(SecurityFrameworkUtils.class)) {
mocked.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(true);
PageResult<PurchaseRecordRespVO> pageResult = new PageResult<>(); PageResult<PurchaseRecordRespVO> pageResult = new PageResult<>();
pageResult.setTotal(0L); pageResult.setTotal(0L);
pageResult.setList(Collections.emptyList()); pageResult.setList(Collections.emptyList());
@@ -110,64 +103,54 @@ class PurchaseControllerTest extends BaseWebUnitTest {
verify(purchaseService, times(1)) verify(purchaseService, times(1))
.getPurchaseRecord(any()); .getPurchaseRecord(any());
}
} }
/** /**
* 非管理端:导出购买记录 * 非管理端:导出购买记录
*/ */
@Test @Test
@WithMockLoginUser(organId = 1L)
void testExportPurchaseRecord_nonManage() throws Exception { void testExportPurchaseRecord_nonManage() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> mocked = Mockito.mockStatic(SecurityFrameworkUtils.class)) { PurchaseRecordRespVO respVO = new PurchaseRecordRespVO();
respVO.setId(1L);
respVO.setOrganId(1L);
mocked.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(false); PageResult<PurchaseRecordRespVO> pageResult = new PageResult<>();
mocked.when(SecurityFrameworkUtils::getUserOrganId).thenReturn(1L); pageResult.setTotal(1L);
pageResult.setList(List.of(respVO));
PurchaseRecordRespVO respVO = new PurchaseRecordRespVO(); when(purchaseService.getPurchaseRecord(any()))
respVO.setId(1L); .thenReturn(pageResult);
respVO.setOrganId(1L);
PageResult<PurchaseRecordRespVO> pageResult = new PageResult<>(); mockMvc.perform(get("/system/purchase/export")
pageResult.setTotal(1L); .contentType(MediaType.APPLICATION_JSON))
pageResult.setList(List.of(respVO)); .andExpect(status().isOk());
when(purchaseService.getPurchaseRecord(any())) verify(purchaseService, times(1))
.thenReturn(pageResult); .getPurchaseRecord(any());
mockMvc.perform(get("/system/purchase/export")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
verify(purchaseService, times(1))
.getPurchaseRecord(any());
}
} }
//
/** /**
* 管理端:导出购买记录 * 管理端:导出购买记录
*/ */
@Test @Test
@WithMockLoginUser(userType = 2)
void testExportPurchaseRecord_manage() throws Exception { void testExportPurchaseRecord_manage() throws Exception {
try (MockedStatic<SecurityFrameworkUtils> mocked = Mockito.mockStatic(SecurityFrameworkUtils.class)) { PurchaseRecordRespVO respVO = new PurchaseRecordRespVO();
respVO.setId(1L);
mocked.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(true); PageResult<PurchaseRecordRespVO> pageResult = new PageResult<>();
pageResult.setTotal(1L);
pageResult.setList(List.of(respVO));
PurchaseRecordRespVO respVO = new PurchaseRecordRespVO(); when(purchaseService.getPurchaseRecord(any()))
respVO.setId(1L); .thenReturn(pageResult);
PageResult<PurchaseRecordRespVO> pageResult = new PageResult<>(); mockMvc.perform(get("/system/purchase/export")
pageResult.setTotal(1L); .contentType(MediaType.APPLICATION_JSON))
pageResult.setList(List.of(respVO)); .andExpect(status().isOk());
when(purchaseService.getPurchaseRecord(any())) verify(purchaseService, times(1))
.thenReturn(pageResult); .getPurchaseRecord(any());
mockMvc.perform(get("/system/purchase/export")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
verify(purchaseService, times(1))
.getPurchaseRecord(any());
}
} }
} }
@@ -0,0 +1,340 @@
package com.cf.imes.module.system.controller.admin.permission;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.framework.web.config.ChenfengWebAutoConfiguration;
import com.cf.imes.module.infra.api.logger.ApiErrorLogApi;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.*;
import com.cf.imes.module.system.dal.dataobject.permission.MenuDO;
import com.cf.imes.module.system.dal.dataobject.permission.RoleDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import com.cf.imes.module.system.enums.permission.MenuSourceTypeEnum;
import com.cf.imes.module.system.enums.permission.MenuTypeEnum;
import com.cf.imes.module.system.service.permission.MenuService;
import com.cf.imes.module.system.service.permission.PermissionService;
import com.cf.imes.module.system.service.permission.RoleService;
import com.cf.imes.module.system.service.user.AdminUserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.util.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* 菜单管理单元测试
*
* @author Gqr
* @since 2026/1/26 16:40
*/
@WebMvcTest(controllers = MenuController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
@ExtendWith(WithMockLoginUserExtension.class)
@Import(ChenfengWebAutoConfiguration.class)
@TestPropertySource(properties = {
"spring.application.name=test-app"
})
class MenuControllerTest extends BaseWebUnitTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private MenuService menuService;
@MockitoBean
private PermissionService permissionService;
@MockitoBean
private AdminUserService userService;
@MockitoBean
private RoleService roleService;
@Autowired
private ObjectMapper objectMapper;
@MockitoBean
private ApiErrorLogApi apiErrorLogApi;
@Test
void createMenu_success() throws Exception {
MenuSaveVO reqVO = new MenuSaveVO();
reqVO.setName("新增菜单");
reqVO.setParentId(0L);
reqVO.setType(MenuTypeEnum.MENU.getType());
reqVO.setSort(1);
reqVO.setParentId(MenuDO.ID_ROOT);
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setSourceType(MenuSourceTypeEnum.PRODUCTION.getType());
when(menuService.createMenu(any(MenuSaveVO.class)))
.thenReturn(100L);
mockMvc.perform(post("/admin-api/system/menu/create")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(100L));
}
@Test
void updateMenu_success() throws Exception {
MenuSaveVO reqVO = new MenuSaveVO();
reqVO.setId(1L);
reqVO.setName("更新菜单");
reqVO.setType(MenuTypeEnum.MENU.getType());
reqVO.setSort(1);
reqVO.setParentId(MenuDO.ID_ROOT);
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setSourceType(MenuSourceTypeEnum.PRODUCTION.getType());
mockMvc.perform(put("/admin-api/system/menu/update")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
verify(menuService).updateMenu(any(MenuSaveVO.class));
}
@Test
void deleteMenu_success() throws Exception {
mockMvc.perform(delete("/admin-api/system/menu/delete")
.param("id", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
verify(menuService).deleteMenu(1L);
}
@Test
@WithMockLoginUser(userId = 1L, organId = 100L)
void getCustomMenuList_success() throws Exception {
// 准备模拟数据
List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));
AdminUserDO user = new AdminUserDO();
user.setId(1L);
Set<Long> roleIds = new HashSet<>();
roleIds.add(1L);
List<RoleDO> roles = Arrays.asList(createRoleDO(1L, CommonStatusEnum.ENABLE.getStatus()));
Set<Long> menuIds = new HashSet<>();
menuIds.add(1L);
when(userService.getUser(1L)).thenReturn(user);
when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds);
when(roleService.getRoleList1(roleIds)).thenReturn(roles);
when(permissionService.getRoleMenuListByRoleId2(any(Set.class), eq(100L))).thenReturn(menuIds);
when(menuService.getCustomEndPointMenuList(menuIds)).thenReturn(menuList);
mockMvc.perform(get("/admin-api/system/menu/custom/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray());
verify(userService).getUser(1L);
verify(permissionService).getUserRoleIdListByUserId(1L);
verify(roleService).getRoleList1(roleIds);
verify(permissionService).getRoleMenuListByRoleId2(any(Set.class), eq(100L));
verify(menuService).getCustomEndPointMenuList(menuIds);
}
@Test
@WithMockLoginUser(userId = 1L, organId = 100L, isSuperAdmin = true)
void getMenuList_superAdmin_success() throws Exception {
// 准备模拟数据
List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));
when(menuService.getMenuList(any(MenuListReqVO.class))).thenReturn(menuList);
mockMvc.perform(get("/admin-api/system/menu/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray());
verify(menuService).getMenuList(any(MenuListReqVO.class));
}
@Test
@WithMockLoginUser(userId = 1L, organId = 100L)
void getMenuList_notsuperAdmin_getUserNull() throws Exception {
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
when(userService.getUser(loginUserId)).thenReturn(null);
mockMvc.perform(get("/admin-api/system/menu/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isEmpty());
}
@Test
@WithMockLoginUser(userId = 1L, organId = 100L)
void getMenuList_notsuperAdmin_getRolesEmpty() throws Exception {
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
when(userService.getUser(loginUserId)).thenReturn(new AdminUserDO());
when(permissionService.getUserRoleIdListByUserId(loginUserId)).thenReturn(null);
mockMvc.perform(get("/admin-api/system/menu/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isEmpty());
}
@Test
@WithMockLoginUser(userId = 1L, organId = 100L)
void getMenuList_normalUser_success() throws Exception {
// 准备模拟数据
List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));
AdminUserDO user = new AdminUserDO();
user.setId(1L);
Set<Long> roleIds = new HashSet<>();
roleIds.add(1L);
List<RoleDO> roles = Arrays.asList(createRoleDO(1L, CommonStatusEnum.ENABLE.getStatus()));
Set<Long> menuIds = new HashSet<>();
menuIds.add(1L);
when(userService.getUser(1L)).thenReturn(user);
when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds);
when(roleService.getRoleList1(roleIds)).thenReturn(roles);
when(permissionService.getRoleMenuListByRoleId2(any(Set.class), eq(100L))).thenReturn(menuIds);
when(menuService.getMenuList1(menuIds)).thenReturn(menuList);
mockMvc.perform(get("/admin-api/system/menu/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray());
verify(userService).getUser(1L);
verify(permissionService).getUserRoleIdListByUserId(1L);
verify(roleService).getRoleList1(roleIds);
verify(permissionService).getRoleMenuListByRoleId2(any(Set.class), eq(100L));
verify(menuService).getMenuList1(menuIds);
}
@Test
void getMenuList_noLoginUser_failure() throws Exception {
mockMvc.perform(get("/admin-api/system/menu/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(GlobalErrorCodeConstants.UNAUTHORIZED.getCode()));
}
@Test
void getCustomMenuList_noLoginUser_failure() throws Exception {
mockMvc.perform(get("/admin-api/system/menu/custom/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(GlobalErrorCodeConstants.UNAUTHORIZED.getCode()));
}
@Test
@WithMockLoginUser
void getCustomMenuList_getUserNull() throws Exception {
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
when(userService.getUser(loginUserId)).thenReturn(null);
mockMvc.perform(get("/admin-api/system/menu/custom/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").isEmpty());
}
@Test
@WithMockLoginUser
void getCustomMenuList_getRolesEmpty() throws Exception {
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
when(userService.getUser(loginUserId)).thenReturn(new AdminUserDO());
when(permissionService.getUserRoleIdListByUserId(loginUserId)).thenReturn(null);
mockMvc.perform(get("/admin-api/system/menu/custom/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").isEmpty());
}
@Test
void getSimpleMenuList_success() throws Exception {
List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));
when(menuService.getMenuListByOrgan(any(MenuListReqVO.class))).thenReturn(menuList);
mockMvc.perform(get("/admin-api/system/menu/list-all-simple"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").isArray());
verify(menuService).getMenuListByOrgan(any(MenuListReqVO.class));
}
@Test
void getMenu_success() throws Exception {
MenuDO menuDO = createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus());
when(menuService.getMenu(1L)).thenReturn(menuDO);
mockMvc.perform(get("/admin-api/system/menu/get")
.param("id", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data.id").value(1L));
verify(menuService).getMenu(1L);
}
@Test
void trans_success() throws Exception {
MenuTransReqVO reqVO = new MenuTransReqVO();
reqVO.setTargetLanguage("en");
mockMvc.perform(post("/admin-api/system/menu/trans")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true));
verify(menuService).trans(any(MenuTransReqVO.class));
}
// 辅助方法:创建MenuDO实例
private MenuDO createMenuDO(Long id, Integer status) {
MenuDO menuDO = new MenuDO();
menuDO.setId(id);
menuDO.setStatus(status);
menuDO.setName("测试菜单");
menuDO.setSort(0);
return menuDO;
}
// 辅助方法:创建RoleDO实例
private RoleDO createRoleDO(Long id, Integer status) {
RoleDO roleDO = new RoleDO();
roleDO.setId(id);
roleDO.setStatus(status);
return roleDO;
}
}
@@ -1,74 +1,189 @@
package com.cf.imes.module.system.service.permission; package com.cf.imes.module.system.service.permission;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest; import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuListReqVO; import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuSaveVO; import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuTransReqVO;
import com.cf.imes.module.system.dal.dataobject.permission.MenuDO; import com.cf.imes.module.system.dal.dataobject.permission.MenuDO;
import com.cf.imes.module.system.dal.dataobject.permission.MenuI18nDO;
import com.cf.imes.module.system.dal.mysql.permission.MenuI18nMapper;
import com.cf.imes.module.system.dal.mysql.permission.MenuMapper; import com.cf.imes.module.system.dal.mysql.permission.MenuMapper;
import com.cf.imes.module.system.enums.common.LanguageEnum;
import com.cf.imes.module.system.enums.permission.MenuSourceTypeEnum; import com.cf.imes.module.system.enums.permission.MenuSourceTypeEnum;
import com.cf.imes.module.system.enums.permission.MenuTypeEnum; import com.cf.imes.module.system.enums.permission.MenuTypeEnum;
import com.cf.imes.module.system.framework.trans.config.TransApi;
import com.cf.imes.module.system.framework.trans.config.TransProperties;
import com.cf.imes.module.system.service.organ.OrganService; import com.cf.imes.module.system.service.organ.OrganService;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.context.annotation.Import; import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import jakarta.annotation.Resource; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static com.cf.imes.framework.common.util.collection.SetUtils.asSet; import static com.cf.imes.framework.common.util.collection.SetUtils.asSet;
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals; import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException; import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.*; import static com.cf.imes.framework.test.core.util.RandomUtils.*;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Import(MenuServiceImpl.class) @ExtendWith(WithMockLoginUserExtension.class)
public class MenuServiceImplTest extends BaseDbUnitTest { class MenuServiceImplTest extends BaseMockitoUnitTest {
@Resource @InjectMocks
private MenuServiceImpl menuService; private MenuServiceImpl menuService;
@Resource @Mock
private MenuMapper menuMapper; private MenuMapper menuMapper;
@MockBean @Mock
private PermissionService permissionService; private PermissionService permissionService;
@MockBean
private OrganService tenantService; @Mock
private OrganService organService;
@Mock
private RedisTemplate redisTemplate;
@Mock
private Cursor<String> cursor;
@Mock
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Mock
private MenuI18nMapper menuI18nMapper;
@Mock
private TransProperties transProperties;
@Test @Test
public void testCreateMenu_success() { @WithMockLoginUser(organId = 1L)
void testCreateMenu_success() {
// mock 数据(构造父菜单) // mock 数据(构造父菜单)
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
"parent", 0L); "parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId(); Long parentId = menuDO.getId();
when(menuMapper.selectById(parentId)).thenReturn(menuDO);
// 校验自己
when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(null);
// 模拟 Mapper 插入时生成 ID
doAnswer(invocation -> {
MenuDO mdo = invocation.getArgument(0);
mdo.setId(100L); // 模拟自增主键
return null;
}).when(menuMapper).insert(any(MenuDO.class));
// 准备参数 // 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
o.setParentId(parentId); o.setParentId(parentId);
o.setName("testSonName"); o.setType(MenuTypeEnum.BUTTON.getType());
o.setType(MenuTypeEnum.MENU.getType());
}).setId(null); // 防止 id 被赋值 }).setId(null); // 防止 id 被赋值
// redis mock
when(redisTemplate.scan(any())).thenReturn(cursor);
when(cursor.hasNext()).thenReturn(true, false);
// mock executor
doAnswer((InvocationOnMock invocation) -> {
((Runnable) invocation.getArguments()[0]).run();
return null;
}).when(threadPoolTaskExecutor).execute(any(Runnable.class));
Long menuId = menuService.createMenu(reqVO); Long menuId = menuService.createMenu(reqVO);
// 校验记录的属性是否正确 // 断言
MenuDO dbMenu = menuMapper.selectById(menuId); assertEquals(100L, menuId);
assertPojoEquals(reqVO, dbMenu, "id"); verify(redisTemplate, times(1)).convertAndSend(com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, "");
verify(redisTemplate, times(2)).scan(any());
verify(menuMapper).insert(argThat((MenuDO menuArg) ->
ObjectUtil.equals(menuArg.getName(), reqVO.getName())
&& ObjectUtil.equals(menuArg.getPermission(), reqVO.getPermission())
&& ObjectUtil.equals(menuArg.getType(), reqVO.getType())
&& ObjectUtil.equals(menuArg.getSort(), reqVO.getSort())
&& ObjectUtil.equals(menuArg.getParentId(), reqVO.getParentId())
&& ObjectUtil.equals("", menuArg.getPath())
&& ObjectUtil.equals("", menuArg.getIcon())
&& ObjectUtil.equals("", menuArg.getComponent())
&& ObjectUtil.equals("", menuArg.getComponentName())
&& ObjectUtil.equals(menuArg.getStatus(), reqVO.getStatus())
&& ObjectUtil.equals(menuArg.getVisible(), reqVO.getVisible())
&& ObjectUtil.equals(menuArg.getKeepAlive(), reqVO.getKeepAlive())
&& ObjectUtil.equals(menuArg.getAlwaysShow(), reqVO.getAlwaysShow())
&& ObjectUtil.equals(menuArg.getDescription(), reqVO.getDescription())
&& ObjectUtil.equals(menuArg.getSourceType(), reqVO.getSourceType())
));
} }
@Test @Test
public void testUpdateMenu_success() { @WithMockLoginUser
void testCreateMenu_fail_sourceTypePermissionError() {
// mock 数据(构造父菜单)
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
"parent", 0L);
Long parentId = menuDO.getId();
when(menuMapper.selectById(parentId)).thenReturn(menuDO);
// 校验自己
when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(null);
// 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
o.setParentId(parentId);
o.setSourceType(MenuSourceTypeEnum.MANAGEMENT.getType());
}).setId(null); // 防止 id 被赋值
assertServiceException(() -> menuService.createMenu(reqVO), MANAGEMENT_MENU_OPERATION_PERMISSION_ERROR);
}
@Test
@WithMockLoginUser
void testUpdateMenu_success() {
// 校验父级
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
"parent", 0L);
// mock 数据(构造父子菜单) // mock 数据(构造父子菜单)
MenuDO sonMenuDO = createParentAndSonMenu(); MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
menuDO.getParentId());
when(menuMapper.selectById(any())).thenReturn(sonMenuDO).thenReturn(menuDO);
when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(sonMenuDO);
Long sonId = sonMenuDO.getId(); Long sonId = sonMenuDO.getId();
// 准备参数 // 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
@@ -76,17 +191,67 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
o.setName("testSonName"); // 修改名字 o.setName("testSonName"); // 修改名字
o.setParentId(sonMenuDO.getParentId()); o.setParentId(sonMenuDO.getParentId());
o.setType(MenuTypeEnum.MENU.getType()); o.setType(MenuTypeEnum.MENU.getType());
o.setPermission(null);
});
// redis mock
when(redisTemplate.scan(any())).thenReturn(cursor);
when(cursor.hasNext()).thenReturn(true, false);
// mock executor
doAnswer((InvocationOnMock invocation) -> {
((Runnable) invocation.getArguments()[0]).run();
return null;
}).when(threadPoolTaskExecutor).execute(any(Runnable.class));
// 调用
menuService.updateMenu(reqVO);
// 校验记录的属性是否正确
verify(menuMapper, times(1)).updateById(any(MenuDO.class));
verify(redisTemplate, times(2)).scan(any());
verify(redisTemplate, times(1)).convertAndSend(com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, "");
}
@Test
@WithMockLoginUser
void testUpdateMenu_success_flushCache() {
// 校验父级
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
"parent", 0L);
// mock 数据(构造父子菜单)
MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
menuDO.getParentId());
// same permission
sonMenuDO.setPermission("permission");
when(menuMapper.selectById(any())).thenReturn(sonMenuDO).thenReturn(menuDO);
when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(sonMenuDO);
Long sonId = sonMenuDO.getId();
// 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
o.setId(sonId);
o.setName("testSonName"); // 修改名字
o.setParentId(sonMenuDO.getParentId());
o.setType(MenuTypeEnum.MENU.getType());
// same permission
o.setPermission("permission");
}); });
// 调用 // 调用
menuService.updateMenu(reqVO); menuService.updateMenu(reqVO);
// 校验记录的属性是否正确 // 校验记录的属性是否正确
MenuDO dbMenu = menuMapper.selectById(sonId); verify(menuMapper, times(1)).updateById(any(MenuDO.class));
assertPojoEquals(reqVO, dbMenu); verify(redisTemplate, never()).scan(any());
verify(redisTemplate, never()).convertAndSend(anyString(), anyString());
} }
@Test @Test
public void testUpdateMenu_sonIdNotExist() { void testUpdateMenu_sonIdNotExist() {
// 准备参数 // 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class); MenuSaveVO reqVO = randomPojo(MenuSaveVO.class);
// 调用,并断言异常 // 调用,并断言异常
@@ -94,46 +259,44 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testDeleteMenu_success() { @WithMockLoginUser
void testDeleteMenu_success() {
// mock 数据 // mock 数据
MenuDO menuDO = randomPojo(MenuDO.class); MenuDO menuDO = randomPojo(MenuDO.class);
menuMapper.insert(menuDO);
// 准备参数 // 准备参数
Long id = menuDO.getId(); Long id = menuDO.getId();
when(menuMapper.selectCountByParentId(id)).thenReturn(0L);
when(menuMapper.selectById(id)).thenReturn(menuDO);
// 调用 // 调用
menuService.deleteMenu(id); menuService.deleteMenu(id);
// 断言 // 断言
MenuDO dbMenuDO = menuMapper.selectById(id); verify(menuMapper, times(1)).deleteById(id);
assertNull(dbMenuDO); verify(permissionService, times(1)).processMenuDeleted(id);
verify(permissionService).processMenuDeleted(id);
} }
@Test @Test
public void testDeleteMenu_menuNotExist() { void testDeleteMenu_menuNotExist() {
assertServiceException(() -> menuService.deleteMenu(randomLongId()), assertServiceException(() -> menuService.deleteMenu(randomLongId()),
MENU_NOT_EXISTS); MENU_NOT_EXISTS);
} }
@Test @Test
public void testDeleteMenu_existChildren() { void testDeleteMenu_existChildren() {
// mock 数据(构造父子菜单) Long menuId = 100L;
MenuDO sonMenu = createParentAndSonMenu(); when(menuMapper.selectCountByParentId(menuId)).thenReturn(1L);
// 准备参数
Long parentId = sonMenu.getParentId();
// 调用并断言异常 // 调用并断言异常
assertServiceException(() -> menuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN); assertServiceException(() -> menuService.deleteMenu(menuId), MENU_EXISTS_CHILDREN);
} }
@Test @Test
public void testGetMenuList_all() { void testGetMenuList_all() {
// mock 数据 // mock 数据
MenuDO menu100 = randomPojo(MenuDO.class); MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class); MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数 when(menuMapper.selectList()).thenReturn(List.of(menu100, menu101));
// 调用 // 调用
List<MenuDO> list = menuService.getMenuList(); List<MenuDO> list = menuService.getMenuList();
@@ -144,16 +307,13 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetMenuList() { void testGetMenuList() {
// mock 数据 // mock 数据
MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setName("晨丰").setStatus(CommonStatusEnum.ENABLE.getStatus())); MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setName("晨丰").setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuMapper.insert(menuDO);
// 测试 status 不匹配
menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 name 不匹配
menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setName("")));
// 准备参数 // 准备参数
MenuListReqVO reqVO = new MenuListReqVO().setName("").setStatus(CommonStatusEnum.ENABLE.getStatus()); MenuListReqVO reqVO = new MenuListReqVO().setName("").setStatus(CommonStatusEnum.ENABLE.getStatus());
when(menuMapper.selectList(reqVO)).thenReturn(List.of(menuDO));
// 调用 // 调用
List<MenuDO> result = menuService.getMenuList(reqVO); List<MenuDO> result = menuService.getMenuList(reqVO);
@@ -163,22 +323,21 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetMenuListByTenant() { void testGetMenuListByTenant() {
// mock 数据 // mock 数据
MenuDO menu100 = randomPojo(MenuDO.class, o -> o.setId(100L).setStatus(CommonStatusEnum.ENABLE.getStatus())); 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())); 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())); MenuDO menu102 = randomPojo(MenuDO.class, o -> o.setId(102L).setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuMapper.insert(menu102);
// mock 过滤菜单 // mock 过滤菜单
Set<Long> menuIds = asSet(100L, 101L); Set<Long> menuIds = asSet(100L);
doNothing().when(tenantService).handleOrganMenu(argThat(handler -> { doNothing().when(organService).handleOrganMenu(argThat(handler -> {
handler.handle(menuIds); handler.handle(menuIds);
return true; return true;
})); }));
// 准备参数 // 准备参数
MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()); MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus());
when(menuMapper.selectList(reqVO)).thenReturn(new ArrayList<>(Arrays.asList(menu100, menu101, menu102)));
// 调用 // 调用
List<MenuDO> result = menuService.getMenuListByOrgan(reqVO); List<MenuDO> result = menuService.getMenuListByOrgan(reqVO);
@@ -188,15 +347,32 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetMenuIdListByPermissionFromCache() { void testGetMenuList1_empty() {
List<MenuDO> menuList1 = menuService.getMenuList1(Collections.emptyList());
assertEquals(0, menuList1.size());
}
@Test
void testGetMenuList1_success() {
MenuDO menuDO = randomPojo(MenuDO.class);
Set<Long> ids = Collections.singleton(menuDO.getId());
when(menuMapper.selectByIds(ids)).thenReturn(List.of(menuDO));
List<MenuDO> menuList1 = menuService.getMenuList1(ids);
assertEquals(menuDO, menuList1.get(0));
}
@Test
void testGetMenuIdListByPermissionFromCache() {
// mock 数据 // mock 数据
MenuDO menu100 = randomPojo(MenuDO.class); MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数 // 准备参数
String permission = menu100.getPermission(); String permission = menu100.getPermission();
when(menuMapper.selectListByPermission(menu100.getPermission())).thenReturn(List.of(menu100));
// 调用 // 调用
List<Long> ids = menuService.getMenuIdListByPermissionFromCache(permission); List<Long> ids = menuService.getMenuIdListByPermissionFromCache(permission);
// 断言 // 断言
@@ -205,14 +381,13 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetMenuList_ids() { void testGetMenuList_ids() {
// mock 数据 // mock 数据
MenuDO menu100 = randomPojo(MenuDO.class); MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数 // 准备参数
Collection<Long> ids = Collections.singleton(menu100.getId()); Collection<Long> ids = Collections.singleton(menu100.getId());
when(menuMapper.selectByIds(ids)).thenReturn(List.of(menu100));
// 调用 // 调用
List<MenuDO> list = menuService.getMenuList(ids); List<MenuDO> list = menuService.getMenuList(ids);
@@ -222,12 +397,13 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetMenu() { void testGetMenu() {
// mock 数据 // mock 数据
MenuDO menu = randomPojo(MenuDO.class); MenuDO menu = randomPojo(MenuDO.class);
menuMapper.insert(menu);
// 准备参数 // 准备参数
Long id = menu.getId(); Long id = menu.getId();
when(menuMapper.selectById(id)).thenReturn(menu);
// 调用 // 调用
MenuDO dbMenu = menuService.getMenu(id); MenuDO dbMenu = menuService.getMenu(id);
@@ -236,38 +412,41 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testValidateParentMenu_success() { void testValidateParentMenu_success() {
// mock 数据 // mock 数据
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
// 准备参数 // 准备参数
Long parentId = menuDO.getId(); Long parentId = menuDO.getId();
when(menuMapper.selectById(parentId)).thenReturn(menuDO);
// 调用,无需断言 // 调用,无需断言
menuService.validateParentMenu(parentId, null); menuService.validateParentMenu(parentId, null);
menuService.validateParentMenu(null, null);
} }
@Test @Test
public void testValidateParentMenu_canNotSetSelfToBeParent() { void testValidateParentMenu_canNotSetSelfToBeParent() {
// 调用,并断言异常 // 调用,并断言异常
assertServiceException(() -> menuService.validateParentMenu(1L, 1L), assertServiceException(() -> menuService.validateParentMenu(1L, 1L),
MENU_PARENT_ERROR); MENU_PARENT_ERROR);
} }
@Test @Test
public void testValidateParentMenu_parentNotExist() { void testValidateParentMenu_parentNotExist() {
// 调用,并断言异常 // 调用,并断言异常
assertServiceException(() -> menuService.validateParentMenu(randomLongId(), null), assertServiceException(() -> menuService.validateParentMenu(randomLongId(), null),
MENU_PARENT_NOT_EXISTS); MENU_PARENT_NOT_EXISTS);
} }
@Test @Test
public void testValidateParentMenu_parentTypeError() { void testValidateParentMenu_parentTypeError() {
// mock 数据 // mock 数据
MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);
menuMapper.insert(menuDO);
// 准备参数 // 准备参数
Long parentId = menuDO.getId(); Long parentId = menuDO.getId();
when(menuMapper.selectById(parentId)).thenReturn(menuDO);
// 调用,并断言异常 // 调用,并断言异常
assertServiceException(() -> menuService.validateParentMenu(parentId, null), assertServiceException(() -> menuService.validateParentMenu(parentId, null),
@@ -275,9 +454,9 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testValidateMenu_success() { void testValidateMenu_success() {
// mock 父子菜单 // mock 父子菜单
MenuDO sonMenu = createParentAndSonMenu(); MenuDO sonMenu = randomPojo(MenuDO.class);
// 准备参数 // 准备参数
Long parentId = sonMenu.getParentId(); Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId(); Long otherSonMenuId = randomLongId();
@@ -288,44 +467,221 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testValidateMenu_sonMenuNameDuplicate() { void testValidateMenu_sonMenuNameDuplicate() {
// mock 父子菜单 // mock 父子菜单
MenuDO sonMenu = createParentAndSonMenu(); MenuDO sonMenu = randomPojo(MenuDO.class);
// 准备参数 // 准备参数
Long parentId = sonMenu.getParentId(); Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId(); Long otherSonMenuId = randomLongId();
String otherSonMenuName = sonMenu.getName(); //相同名称 String otherSonMenuName = sonMenu.getName(); //相同名称
when(menuMapper.selectByParentIdAndNameAndSourceType(parentId, otherSonMenuName, MenuSourceTypeEnum.PRODUCTION.getType())).thenReturn(sonMenu);
// 调用,并断言异常 // 调用,并断言异常
assertServiceException(() -> menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId, MenuSourceTypeEnum.PRODUCTION.getType()), assertServiceException(() -> menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId, MenuSourceTypeEnum.PRODUCTION.getType()),
MENU_NAME_DUPLICATE); MENU_NAME_DUPLICATE);
} }
// ====================== 初始化方法 ====================== @Test
void testValidateMenu_sonMenuNameDuplicate_whenNull() {
// mock 父子菜单
MenuDO sonMenu = randomPojo(MenuDO.class);
// 准备参数
Long parentId = sonMenu.getParentId();
String otherSonMenuName = sonMenu.getName(); //相同名称
/** when(menuMapper.selectByParentIdAndNameAndSourceType(parentId, otherSonMenuName, MenuSourceTypeEnum.PRODUCTION.getType())).thenReturn(sonMenu);
* 插入父子菜单,返回子菜单
* // 调用,并断言异常
* @return 子菜单 assertServiceException(() -> menuService.validateMenu(parentId, otherSonMenuName, null, MenuSourceTypeEnum.PRODUCTION.getType()),
*/ MENU_NAME_DUPLICATE);
private MenuDO createParentAndSonMenu() {
// 构造父子菜单
MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", MenuDO.ID_ROOT);
menuMapper.insert(parentMenuDO);
// 构建子菜单
MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
parentMenuDO.getParentId());
menuMapper.insert(sonMenuDO);
return sonMenuDO;
} }
@Test
void testGetManageEndPointMenuList() {
List<MenuDO> mockList = List.of(new MenuDO());
when(menuMapper.selectListIn(
anyCollection(),
eq(true),
anyString()
)).thenReturn(mockList);
List<MenuDO> result = menuService.getManageEndPointMenuList(List.of(1L, 2L));
assertSame(mockList, result);
verify(menuMapper).selectListIn(
anyCollection(),
eq(true),
anyString()
);
}
@Test
void testGetCustomEndPointMenuList() {
List<MenuDO> mockList = List.of(new MenuDO());
when(menuMapper.selectListIn(
anyCollection(),
eq(false),
anyString()
)).thenReturn(mockList);
List<MenuDO> result = menuService.getCustomEndPointMenuList(List.of(1L, 2L));
assertSame(mockList, result);
verify(menuMapper).selectListIn(
anyCollection(),
eq(false),
anyString()
);
}
// ====================== 初始化方法 ======================
private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) { private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) {
return buildMenuDO(type, name, parentId, randomCommonStatus()); return buildMenuDO(type, name, parentId, randomCommonStatus());
} }
private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) {
return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId) return randomPojo(MenuDO.class, o -> o.setName(name).setParentId(parentId)
.setType(type.getType()).setStatus(status)); .setType(type.getType()).setStatus(status));
} }
@Test
void testTrans_errorCode() {
// mock properties
when(transProperties.getAppId()).thenReturn("appId");
when(transProperties.getSecurityKey()).thenReturn("key");
when(transProperties.getApiHost()).thenReturn("host");
MenuTransReqVO req = new MenuTransReqVO();
req.setTargetLanguage("en");
when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(randomPojo(MenuDO.class)));
String errorJson = """
{
"error_code": "54001"
}
""";
try (MockedConstruction<TransApi> mocked =
Mockito.mockConstruction(TransApi.class,
(mock, context) -> when(mock.getTransResult(any(), any(), any()))
.thenReturn(errorJson))) {
assertThrows(ServiceException.class,
() -> menuService.trans(req));
}
verify(menuI18nMapper, never()).insertBatch(any());
}
@Test
void testTrans_success_en() {
// mock properties
when(transProperties.getAppId()).thenReturn("appId");
when(transProperties.getSecurityKey()).thenReturn("key");
when(transProperties.getApiHost()).thenReturn("host");
MenuTransReqVO req = new MenuTransReqVO();
req.setTargetLanguage("en");
when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(randomPojo(MenuDO.class, o -> {
o.setName("系统管理");
})));
String okJson = """
{
"trans_result": [
{ "src": "系统管理", "dst": "system management" }
]
}
""";
try (MockedConstruction<TransApi> mocked =
Mockito.mockConstruction(TransApi.class,
(mock, context) -> when(mock.getTransResult(any(), any(), any()))
.thenReturn(okJson))) {
menuService.trans(req);
}
ArgumentCaptor<List<MenuI18nDO>> captor =
ArgumentCaptor.forClass(List.class);
verify(menuI18nMapper).insertBatch(captor.capture());
MenuI18nDO i18n = captor.getValue().get(0);
assertEquals("System Management", i18n.getMenuName());
assertEquals("en", i18n.getLocale());
}
@Test
void testTrans_srcNotMatch() {
MenuTransReqVO req = new MenuTransReqVO();
req.setTargetLanguage("en");
when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(randomPojo(MenuDO.class, o -> {
o.setName("系统管理");
})));
String json = """
{
"trans_result": [
{ "src": "用户管理", "dst": "user management" }
]
}
""";
try (MockedConstruction<TransApi> mocked =
Mockito.mockConstruction(TransApi.class,
(mock, context) -> when(mock.getTransResult(any(), any(), any()))
.thenReturn(json))) {
menuService.trans(req);
}
verify(menuI18nMapper, never()).insertBatch(any());
}
@Test
void testTrans_partialSuccess() {
MenuTransReqVO req = new MenuTransReqVO();
req.setTargetLanguage(LanguageEnum.ZH.getLanguage());
when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(
List.of(
randomPojo(MenuDO.class, o -> o.setName("系统管理")),
randomPojo(MenuDO.class, o -> o.setName("用户管理"))
)
);
String json = """
{
"trans_result": [
{ "src": "系统管理", "dst": "system management" },
{ "src": "角色管理", "dst": "role management" }
]
}
""";
try (MockedConstruction<TransApi> mocked =
Mockito.mockConstruction(TransApi.class,
(mock, context) -> when(mock.getTransResult(any(), any(), any()))
.thenReturn(json))) {
menuService.trans(req);
}
ArgumentCaptor<List<MenuI18nDO>> captor =
ArgumentCaptor.forClass(List.class);
verify(menuI18nMapper).insertBatch(captor.capture());
assertEquals(1, captor.getValue().size());
}
} }