package com.vincent.rsf.server.system.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.vincent.rsf.framework.common.R; import com.vincent.rsf.server.common.service.RedisService; import com.vincent.rsf.server.system.entity.Menu; import com.vincent.rsf.server.system.service.MenuService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class MenuControllerTest { private static final String MENU_TREE_CACHE_FLAG = "MENU_TREE"; private static final String MENU_TREE_CACHE_KEY = "FULL_TREE"; @Mock private MenuService menuService; @Mock private RedisService redisService; private MenuController menuController; @BeforeEach void setUp() { menuController = new MenuController(); ReflectionTestUtils.setField(menuController, "menuService", menuService); ReflectionTestUtils.setField(menuController, "redisService", redisService); } @Test void treeReturnsCachedTreeBeforeQueryingDatabase() { List cachedTree = List.of(menu(1L, 0L, "System")); when(redisService.get(MENU_TREE_CACHE_FLAG, MENU_TREE_CACHE_KEY)).thenReturn(cachedTree); R result = menuController.tree(new HashMap<>()); assertEquals(cachedTree, result.get("data")); verify(menuService, never()).list(any(LambdaQueryWrapper.class)); } @Test void treeLoadsFromDatabaseAndCachesFullTreeWhenCacheMissing() { when(redisService.get(MENU_TREE_CACHE_FLAG, MENU_TREE_CACHE_KEY)).thenReturn(null); when(menuService.list(any(LambdaQueryWrapper.class))).thenReturn(List.of( menu(1L, 0L, "System"), menu(2L, 1L, "User") )); R result = menuController.tree(new HashMap<>()); List tree = cast(result.get("data")); assertEquals(1, tree.size()); assertNotNull(tree.get(0).getChildren()); assertEquals(1, tree.get(0).getChildren().size()); ArgumentCaptor captor = ArgumentCaptor.forClass(Object.class); verify(redisService).set(eq(MENU_TREE_CACHE_FLAG), eq(MENU_TREE_CACHE_KEY), captor.capture(), anyInt()); List cachedTree = cast(captor.getValue()); assertEquals(1, cachedTree.size()); assertEquals(1L, cachedTree.get(0).getId()); } @Test void saveEvictsMenuTreeCacheAfterSuccessfulSave() { Menu menu = menu(1L, 0L, "System"); when(menuService.save(menu)).thenReturn(true); menuController.save(menu); verify(redisService).delete(MENU_TREE_CACHE_FLAG, MENU_TREE_CACHE_KEY); } @Test void updateEvictsMenuTreeCacheAfterSuccessfulUpdate() { Menu menu = menu(1L, 0L, "System"); when(menuService.updateById(menu)).thenReturn(true); menuController.update(menu); verify(redisService).delete(MENU_TREE_CACHE_FLAG, MENU_TREE_CACHE_KEY); } @Test void removeEvictsMenuTreeCacheAfterSuccessfulRemove() { when(menuService.removeByIds(List.of(1L, 2L))).thenReturn(true); menuController.remove(new Long[]{1L, 2L}); verify(redisService).delete(MENU_TREE_CACHE_FLAG, MENU_TREE_CACHE_KEY); } private Menu menu(Long id, Long parentId, String name) { Menu menu = new Menu(); menu.setId(id); menu.setParentId(parentId); menu.setName(name); menu.setChildren(new ArrayList<>()); return menu; } @SuppressWarnings("unchecked") private List cast(Object value) { return (List) value; } }