完善 MenuService 的单元测试

This commit is contained in:
YunaiV 2023-02-28 00:39:25 +08:00
parent be465d758b
commit 152fc70e86
4 changed files with 36 additions and 142 deletions

View File

@ -62,12 +62,12 @@ public interface MenuService {
List<MenuDO> getMenuList(MenuListReqVO reqVO);
/**
* 获得权限对应的菜单数组
* 获得权限对应的菜单编号数组
*
* @param permission 权限标识
* @return 数组
*/
List<MenuDO> getMenuListByPermissionFromCache(String permission);
List<Long> getMenuIdListByPermissionFromCache(String permission);
/**
* 获得菜单

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
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;
@ -10,9 +11,9 @@ import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import com.baomidou.dynamic.datasource.annotation.Master;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -23,6 +24,7 @@ import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
@ -44,6 +46,7 @@ public class MenuServiceImpl implements MenuService {
private TenantService tenantService;
@Override
@CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#reqVO.permission")
public Long createMenu(MenuCreateReqVO reqVO) {
// 校验父菜单存在
validateParentMenu(reqVO.getParentId(), null);
@ -59,6 +62,8 @@ public class MenuServiceImpl implements MenuService {
}
@Override
@CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST,
allEntries = true) // allEntries 清空所有缓存因为 permission 如果变更涉及到新老两个 permission直接清理简单有效
public void updateMenu(MenuUpdateReqVO reqVO) {
// 校验更新的菜单是否存在
if (menuMapper.selectById(reqVO.getId()) == null) {
@ -77,19 +82,21 @@ public class MenuServiceImpl implements MenuService {
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteMenu(Long menuId) {
@CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST,
allEntries = true) // allEntries 清空所有缓存因为此时不知道 id 对应的 permission 是多少直接清理简单有效
public void deleteMenu(Long id) {
// 校验是否还有子菜单
if (menuMapper.selectCountByParentId(menuId) > 0) {
if (menuMapper.selectCountByParentId(id) > 0) {
throw exception(MENU_EXISTS_CHILDREN);
}
// 校验删除的菜单是否存在
if (menuMapper.selectById(menuId) == null) {
if (menuMapper.selectById(id) == null) {
throw exception(MENU_NOT_EXISTS);
}
// 标记删除
menuMapper.deleteById(menuId);
menuMapper.deleteById(id);
// 删除授予给角色的权限
permissionService.processMenuDeleted(menuId);
permissionService.processMenuDeleted(id);
}
@Override
@ -112,8 +119,9 @@ public class MenuServiceImpl implements MenuService {
@Override
@Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission")
public List<MenuDO> getMenuListByPermissionFromCache(String permission) {
return menuMapper.selectListByPermission(permission);
public List<Long> getMenuIdListByPermissionFromCache(String permission) {
List<MenuDO> menus = menuMapper.selectListByPermission(permission);
return convertList(menus, MenuDO::getId);
}
@Override

View File

@ -7,7 +7,6 @@ import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
@ -206,19 +205,18 @@ public class PermissionServiceImpl implements PermissionService {
* @param permission 权限标识
* @return 是否拥有
*/
// TODO 芋艿要想想咋继续优化
private boolean hasAnyPermission(List<RoleDO> roles, String permission) {
List<MenuDO> menuList = menuService.getMenuListByPermissionFromCache(permission);
List<Long> menuIds = menuService.getMenuIdListByPermissionFromCache(permission);
// 采用严格模式如果权限找不到对应的 Menu 的话也认为没有权限
if (CollUtil.isEmpty(menuList)) {
if (CollUtil.isEmpty(menuIds)) {
return false;
}
// 判断是否有权限
Set<Long> roleIds = convertSet(roles, RoleDO::getId);
for (MenuDO menu : menuList) {
for (Long menuId : menuIds) {
// 拥有该角色的菜单编号数组
Set<Long> menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menu.getId());
Set<Long> menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId);
// 如果有交集说明有权限
if (CollUtil.containsAny(menuRoleIds, roleIds)) {
return true;

View File

@ -8,10 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUp
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 com.google.common.collect.LinkedListMultimap;
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;
@ -26,8 +23,6 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.argThat;
@ -46,35 +41,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
@MockBean
private PermissionService permissionService;
@MockBean
private MenuProducer menuProducer;
@MockBean
private TenantService tenantService;
@Test
public void testInitLocalCache_success() {
MenuDO menuDO1 = randomPojo(MenuDO.class);
menuMapper.insert(menuDO1);
MenuDO menuDO2 = randomPojo(MenuDO.class);
menuMapper.insert(menuDO2);
// 调用
menuService.initLocalCache();
// 校验 menuCache 缓存
Map<Long, MenuDO> menuCache = menuService.getMenuCache();
assertEquals(2, menuCache.size());
assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId()));
assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId()));
// 校验 permissionMenuCache 缓存
Multimap<String, MenuDO> permissionMenuCache = menuService.getPermissionMenuCache();
assertEquals(2, permissionMenuCache.size());
assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission()));
assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission()));
}
@Test
public void testCreateMenu_success() {
// mock 数据构造父菜单
MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU,
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
"parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
@ -89,14 +61,12 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
// 校验记录的属性是否正确
MenuDO dbMenu = menuMapper.selectById(menuId);
assertPojoEquals(reqVO, dbMenu);
// 校验调用
verify(menuProducer).sendMenuRefreshMessage();
}
@Test
public void testUpdateMenu_success() {
// mock 数据构造父子菜单
MenuDO sonMenuDO = initParentAndSonMenu();
MenuDO sonMenuDO = createParentAndSonMenu();
Long sonId = sonMenuDO.getId();
// 准备参数
MenuUpdateReqVO reqVO = randomPojo(MenuUpdateReqVO.class, o -> {
@ -111,8 +81,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
// 校验记录的属性是否正确
MenuDO dbMenu = menuMapper.selectById(sonId);
assertPojoEquals(reqVO, dbMenu);
// 校验调用
verify(menuProducer).sendMenuRefreshMessage();
}
@Test
@ -137,7 +105,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
MenuDO dbMenuDO = menuMapper.selectById(id);
assertNull(dbMenuDO);
verify(permissionService).processMenuDeleted(id);
verify(menuProducer).sendMenuRefreshMessage();
}
@Test
@ -149,7 +116,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
@Test
public void testDeleteMenu_existChildren() {
// mock 数据构造父子菜单
MenuDO sonMenu = initParentAndSonMenu();
MenuDO sonMenu = createParentAndSonMenu();
// 准备参数
Long parentId = sonMenu.getParentId();
@ -218,85 +185,6 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
assertPojoEquals(menu100, result.get(0));
}
@Test
public void testListMenusFromCache_withoutId() {
// mock 缓存
Map<Long, MenuDO> menuCache = new HashMap<>();
// 可被匹配
MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L)
.setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuCache.put(menuDO.getId(), menuDO);
// 测试 type 不匹配
menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L)
.setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())));
// 测试 status 不匹配
menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L)
.setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus())));
menuService.setMenuCache(menuCache);
// 准备参数
Collection<Integer> menuTypes = singletonList(MenuTypeEnum.MENU.getType());
Collection<Integer> menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<MenuDO> list = menuService.getMenuListFromCache(menuTypes, menusStatuses);
// 断言
assertEquals(1, list.size());
assertPojoEquals(menuDO, list.get(0));
}
@Test
public void testListMenusFromCache_withId() {
// mock 缓存
Map<Long, MenuDO> menuCache = new HashMap<>();
// 可被匹配
MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setId(1L)
.setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuCache.put(menuDO.getId(), menuDO);
// 测试 id 不匹配
menuCache.put(2L, randomPojo(MenuDO.class, o -> o.setId(2L)
.setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())));
// 测试 type 不匹配
menuCache.put(3L, randomPojo(MenuDO.class, o -> o.setId(3L)
.setType(MenuTypeEnum.BUTTON.getType()).setStatus(CommonStatusEnum.ENABLE.getStatus())));
// 测试 status 不匹配
menuCache.put(4L, randomPojo(MenuDO.class, o -> o.setId(4L)
.setType(MenuTypeEnum.MENU.getType()).setStatus(CommonStatusEnum.DISABLE.getStatus())));
menuService.setMenuCache(menuCache);
// 准备参数
Collection<Long> menuIds = asList(1L, 3L, 4L);
Collection<Integer> menuTypes = singletonList(MenuTypeEnum.MENU.getType());
Collection<Integer> menusStatuses = singletonList(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<MenuDO> list = menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses);
// 断言
assertEquals(1, list.size());
assertPojoEquals(menuDO, list.get(0));
}
@Test
public void testGetMenuListByPermissionFromCache() {
// mock 缓存
Multimap<String, MenuDO> permissionMenuCache = LinkedListMultimap.create();
// 可被匹配
MenuDO menuDO01 = randomPojo(MenuDO.class, o -> o.setId(1L).setPermission("123"));
permissionMenuCache.put(menuDO01.getPermission(), menuDO01);
MenuDO menuDO02 = randomPojo(MenuDO.class, o -> o.setId(2L).setPermission("123"));
permissionMenuCache.put(menuDO02.getPermission(), menuDO02);
// 不可匹配
permissionMenuCache.put("456", randomPojo(MenuDO.class, o -> o.setId(3L).setPermission("456")));
menuService.setPermissionMenuCache(permissionMenuCache);
// 准备参数
String permission = "123";
// 调用
List<MenuDO> list = menuService.getMenuListByPermissionFromCache(permission);
// 断言
assertEquals(2, list.size());
assertPojoEquals(menuDO01, list.get(0));
assertPojoEquals(menuDO02, list.get(1));
}
@Test
public void testGetMenu() {
// mock 数据
@ -314,7 +202,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
@Test
public void testValidateParentMenu_success() {
// mock 数据
MenuDO menuDO = createMenuDO(MenuTypeEnum.MENU, "parent", 0L);
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
// 准备参数
Long parentId = menuDO.getId();
@ -340,7 +228,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
@Test
public void testValidateParentMenu_parentTypeError() {
// mock 数据
MenuDO menuDO = createMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);
MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);
menuMapper.insert(menuDO);
// 准备参数
Long parentId = menuDO.getId();
@ -353,7 +241,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
@Test
public void testValidateMenu_success() {
// mock 父子菜单
MenuDO sonMenu = initParentAndSonMenu();
MenuDO sonMenu = createParentAndSonMenu();
// 准备参数
Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId();
@ -366,7 +254,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
@Test
public void testValidateMenu_sonMenuNameDuplicate() {
// mock 父子菜单
MenuDO sonMenu = initParentAndSonMenu();
MenuDO sonMenu = createParentAndSonMenu();
// 准备参数
Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId();
@ -380,26 +268,26 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
// ====================== 初始化方法 ======================
/**
* 构造父子菜单返回子菜单
* 插入父子菜单返回子菜单
*
* @return 子菜单
*/
private MenuDO initParentAndSonMenu() {
private MenuDO createParentAndSonMenu() {
// 构造父子菜单
MenuDO parentMenuDO = createMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT);
MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT);
menuMapper.insert(parentMenuDO);
// 构建子菜单
MenuDO sonMenuDO = createMenuDO(MenuTypeEnum.MENU, "testSonName",
MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
parentMenuDO.getParentId());
menuMapper.insert(sonMenuDO);
return sonMenuDO;
}
private MenuDO createMenuDO(MenuTypeEnum type, String name, Long parentId) {
return createMenuDO(type, name, parentId, randomCommonStatus());
private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) {
return buildMenuDO(type, name, parentId, randomCommonStatus());
}
private MenuDO createMenuDO(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)
.setType(type.getType()).setStatus(status));
}