浏览代码

!25 Auth 单元测试
Merge pull request !25 from neilz/feature/ut-auth

芋道源码 4 年之前
父节点
当前提交
c41d712d99

+ 269 - 110
src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthServiceImplTest.java

@@ -1,110 +1,269 @@
-package cn.iocoder.dashboard.modules.system.service.auth;
-
-import cn.iocoder.dashboard.BaseDbUnitTest;
-import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
-import cn.iocoder.dashboard.framework.security.core.LoginUser;
-import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
-import cn.iocoder.dashboard.modules.system.service.auth.impl.SysAuthServiceImpl;
-import cn.iocoder.dashboard.modules.system.service.common.SysCaptchaService;
-import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
-import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
-import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
-import cn.iocoder.dashboard.util.AssertUtils;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Import;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
-
-import javax.annotation.Resource;
-import java.util.Set;
-
-import static cn.iocoder.dashboard.util.RandomUtils.*;
-import static java.util.Collections.singleton;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-/**
- * {@link SysAuthServiceImpl} 的单元测试
- *
- * @author 芋道源码
- */
-@Import(SysAuthServiceImpl.class)
-public class SysAuthServiceImplTest extends BaseDbUnitTest {
-
-    @Resource
-    private SysAuthServiceImpl authService;
-
-    @MockBean
-    private SysUserService userService;
-    @MockBean
-    private SysPermissionService permissionService;
-    @MockBean
-    private AuthenticationManager authenticationManager;
-    @MockBean
-    private SysCaptchaService captchaService;
-    @MockBean
-    private SysLoginLogService loginLogService;
-    @MockBean
-    private SysUserSessionService userSessionService;
-
-    @Test
-    public void testLoadUserByUsername_success() {
-        // 准备参数
-        String username = randomString();
-        // mock 方法
-        SysUserDO user = randomUserDO(o -> o.setUsername(username));
-        when(userService.getUserByUsername(eq(username))).thenReturn(user);
-
-        // 调用
-        LoginUser loginUser = (LoginUser) authService.loadUserByUsername(username);
-        // 校验
-        AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
-        assertNull(loginUser.getRoleIds()); // 此时不会加载角色,所以是空的
-    }
-
-    @Test
-    public void testLoadUserByUsername_userNotFound() {
-        // 准备参数
-        String username = randomString();
-        // mock 方法
-
-        // 调用, 并断言异常
-        assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
-                () -> authService.loadUserByUsername(username),
-                username); // 异常提示为 username
-    }
-
-    @Test
-    public void testMockLogin_success() {
-        // 准备参数
-        Long userId = randomLongId();
-        // mock 方法 01
-        SysUserDO user = randomUserDO(o -> o.setId(userId));
-        when(userService.getUser(eq(userId))).thenReturn(user);
-        // mock 方法 02
-        Set<Long> roleIds = randomSet(Long.class);
-        when(permissionService.getUserRoleIds(eq(userId), eq(singleton(CommonStatusEnum.ENABLE.getStatus()))))
-                .thenReturn(roleIds);
-
-        // 调用
-        LoginUser loginUser = authService.mockLogin(userId);
-        // 断言
-        AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
-        assertEquals(roleIds, loginUser.getRoleIds());
-    }
-
-    @Test
-    public void testMockLogin_userNotFound() {
-        // 准备参数
-        Long userId = randomLongId();
-        // mock 方法
-
-        // 调用, 并断言异常
-        assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
-                () -> authService.mockLogin(userId),
-                String.valueOf(userId)); // 异常提示为 userId
-    }
-
-}
+package cn.iocoder.dashboard.modules.system.service.auth;
+
+import cn.iocoder.dashboard.BaseDbUnitTest;
+import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
+import cn.iocoder.dashboard.framework.security.core.LoginUser;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginLogTypeEnum;
+import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginResultEnum;
+import cn.iocoder.dashboard.modules.system.service.auth.impl.SysAuthServiceImpl;
+import cn.iocoder.dashboard.modules.system.service.common.SysCaptchaService;
+import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
+import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
+import cn.iocoder.dashboard.util.AssertUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
+import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
+import static cn.iocoder.dashboard.util.RandomUtils.*;
+import static java.util.Collections.singleton;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link SysAuthServiceImpl} 的单元测试
+ *
+ * @author 芋道源码
+ */
+@Import(SysAuthServiceImpl.class)
+public class SysAuthServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private SysAuthServiceImpl authService;
+
+    @MockBean
+    private SysUserService userService;
+    @MockBean
+    private SysPermissionService permissionService;
+    @MockBean
+    private AuthenticationManager authenticationManager;
+    @MockBean
+    private Authentication authentication;
+    @MockBean
+    private SysCaptchaService captchaService;
+    @MockBean
+    private SysLoginLogService loginLogService;
+    @MockBean
+    private SysUserSessionService userSessionService;
+
+    @Test
+    public void testLoadUserByUsername_success() {
+        // 准备参数
+        String username = randomString();
+        // mock 方法
+        SysUserDO user = randomUserDO(o -> o.setUsername(username));
+        when(userService.getUserByUsername(eq(username))).thenReturn(user);
+
+        // 调用
+        LoginUser loginUser = (LoginUser) authService.loadUserByUsername(username);
+        // 校验
+        AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
+        assertNull(loginUser.getRoleIds()); // 此时不会加载角色,所以是空的
+    }
+
+    @Test
+    public void testLoadUserByUsername_userNotFound() {
+        // 准备参数
+        String username = randomString();
+        // mock 方法
+
+        // 调用, 并断言异常
+        assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
+                () -> authService.loadUserByUsername(username),
+                username); // 异常提示为 username
+    }
+
+    @Test
+    public void testMockLogin_success() {
+        // 准备参数
+        Long userId = randomLongId();
+        // mock 方法 01
+        SysUserDO user = randomUserDO(o -> o.setId(userId));
+        when(userService.getUser(eq(userId))).thenReturn(user);
+        // mock 方法 02
+        Set<Long> roleIds = randomSet(Long.class);
+        when(permissionService.getUserRoleIds(eq(userId), eq(singleton(CommonStatusEnum.ENABLE.getStatus()))))
+                .thenReturn(roleIds);
+
+        // 调用
+        LoginUser loginUser = authService.mockLogin(userId);
+        // 断言
+        AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
+        assertEquals(roleIds, loginUser.getRoleIds());
+    }
+
+    @Test
+    public void testMockLogin_userNotFound() {
+        // 准备参数
+        Long userId = randomLongId();
+        // mock 方法
+
+        // 调用, 并断言异常
+        assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
+                () -> authService.mockLogin(userId),
+                String.valueOf(userId)); // 异常提示为 userId
+    }
+
+    @Test
+    public void testLogin_captchaNotFound() {
+        // 准备参数
+        SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
+        String userIp = randomString();
+        String userAgent = randomString();
+        // 调用, 并断言异常
+        assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_NOT_FOUND);
+        // 校验调用参数
+        verify(loginLogService, times(1)).createLoginLog(
+            argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
+                    && o.getResult().equals(SysLoginResultEnum.CAPTCHA_NOT_FOUND.getResult()))
+        );
+    }
+
+    @Test
+    public void testLogin_captchaCodeError() {
+        // 准备参数
+        String userIp = randomString();
+        String userAgent = randomString();
+        String code = randomString();
+        SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
+        // mock 验证码不正确
+        when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
+        // 调用, 并断言异常
+        assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
+        // 校验调用参数
+        verify(loginLogService, times(1)).createLoginLog(
+            argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
+                    && o.getResult().equals(SysLoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
+        );
+    }
+
+    @Test
+    public void testLogin_badCredentials() {
+        // 准备参数
+        String userIp = randomString();
+        String userAgent = randomString();
+        SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
+        // mock 验证码正确
+        when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
+        // mock 抛出异常
+        when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
+                .thenThrow(new BadCredentialsException("测试账号或密码不正确"));
+        // 调用, 并断言异常
+        assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS);
+        // 校验调用参数
+        verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
+        verify(loginLogService, times(1)).createLoginLog(
+            argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
+                    && o.getResult().equals(SysLoginResultEnum.BAD_CREDENTIALS.getResult()))
+        );
+    }
+
+    @Test
+    public void testLogin_userDisabled() {
+        // 准备参数
+        String userIp = randomString();
+        String userAgent = randomString();
+        SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
+        // mock 验证码正确
+        when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
+        // mock 抛出异常
+        when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
+                .thenThrow(new DisabledException("测试用户被禁用"));
+        // 调用, 并断言异常
+        assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED);
+        // 校验调用参数
+        verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
+        verify(loginLogService, times(1)).createLoginLog(
+            argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
+                    && o.getResult().equals(SysLoginResultEnum.USER_DISABLED.getResult()))
+        );
+    }
+
+    @Test
+    public void testLogin_unknownError() {
+        // 准备参数
+        String userIp = randomString();
+        String userAgent = randomString();
+        SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
+        // mock 验证码正确
+        when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
+        // mock 抛出异常
+        when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
+                .thenThrow(new AuthenticationException("测试未知异常") {});
+        // 调用, 并断言异常
+        assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN);
+        // 校验调用参数
+        verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
+        verify(loginLogService, times(1)).createLoginLog(
+            argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
+                    && o.getResult().equals(SysLoginResultEnum.UNKNOWN_ERROR.getResult()))
+        );
+    }
+
+    @Test
+    public void testLogin_success() {
+        // 准备参数
+        String userIp = randomString();
+        String userAgent = randomString();
+        Long userId = randomLongId();
+        Set<Long> userRoleIds = randomSet(Long.class);
+        String sessionId = randomString();
+        SysAuthLoginReqVO reqVO = randomPojo(SysAuthLoginReqVO.class);
+        LoginUser loginUser = randomPojo(LoginUser.class, o -> {
+            o.setId(userId);
+            o.setRoleIds(userRoleIds);
+        });
+        // mock 验证码正确
+        when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
+        // mock authentication
+        when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
+                .thenReturn(authentication);
+        when(authentication.getPrincipal()).thenReturn(loginUser);
+        // mock 获得 User 拥有的角色编号数组
+        when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds);
+        // mock 缓存登陆用户到 Redis
+        when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(sessionId);
+        // 调用, 并断言异常
+        String login = authService.login(reqVO, userIp, userAgent);
+        assertEquals(sessionId, login);
+        // 校验调用参数
+        verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid());
+        verify(loginLogService, times(1)).createLoginLog(
+            argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGIN_USERNAME.getType())
+                    && o.getResult().equals(SysLoginResultEnum.SUCCESS.getResult()))
+        );
+    }
+
+    @Test
+    public void testLogout_success() {
+        // 准备参数
+        String token = randomString();
+        LoginUser loginUser = randomPojo(LoginUser.class);
+        // mock
+        when(userSessionService.getLoginUser(token)).thenReturn(loginUser);
+        // 调用
+        authService.logout(token);
+        // 校验调用参数
+        verify(userSessionService, times(1)).deleteUserSession(token);
+        verify(loginLogService, times(1)).createLoginLog(
+            argThat(o -> o.getLogType().equals(SysLoginLogTypeEnum.LOGOUT_SELF.getType())
+                    && o.getResult().equals(SysLoginResultEnum.SUCCESS.getResult()))
+        );
+    }
+
+}

+ 171 - 14
src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserSessionServiceImplTest.java

@@ -1,28 +1,57 @@
 package cn.iocoder.dashboard.modules.system.service.auth;
 
+import static cn.hutool.core.util.RandomUtil.randomEle;
+import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER;
+import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.dashboard.util.RandomUtils.randomDate;
+import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
+import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
+import static cn.iocoder.dashboard.util.RandomUtils.randomString;
+import static cn.iocoder.dashboard.util.date.DateUtils.addTime;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.when;
+
+import java.time.Duration;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.annotation.Resource;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
 import cn.hutool.core.date.DateUtil;
 import cn.iocoder.dashboard.BaseDbAndRedisUnitTest;
+import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
+import cn.iocoder.dashboard.framework.security.core.LoginUser;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobPageReqVO;
+import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
+import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
+import cn.iocoder.dashboard.modules.infra.enums.job.InfJobStatusEnum;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.auth.SysUserSessionMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.user.SysUserMapper;
 import cn.iocoder.dashboard.modules.system.dal.redis.auth.SysLoginUserRedisDAO;
+import cn.iocoder.dashboard.modules.system.enums.common.SysSexEnum;
 import cn.iocoder.dashboard.modules.system.service.auth.impl.SysUserSessionServiceImpl;
 import cn.iocoder.dashboard.modules.system.service.dept.impl.SysDeptServiceImpl;
 import cn.iocoder.dashboard.modules.system.service.logger.impl.SysLoginLogServiceImpl;
 import cn.iocoder.dashboard.modules.system.service.user.SysUserServiceImpl;
 import cn.iocoder.dashboard.util.AssertUtils;
 import cn.iocoder.dashboard.util.RandomUtils;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Import;
-
-import javax.annotation.Resource;
-import java.util.Date;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import cn.iocoder.dashboard.util.json.JsonUtils;
+import cn.iocoder.dashboard.util.object.ObjectUtils;
 
 /**
  * SysUserSessionServiceImpl Tester.
@@ -31,13 +60,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
  * @version 1.0
  * @since <pre>3月 8, 2021</pre>
  */
-@Import(SysUserSessionServiceImpl.class)
+@Import({SysUserSessionServiceImpl.class, SysLoginUserRedisDAO.class})
 public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
 
     @Resource
     private SysUserSessionServiceImpl sysUserSessionService;
     @Resource
     private SysUserSessionMapper sysUserSessionMapper;
+    @Resource
+    private SysLoginUserRedisDAO sysLoginUserRedisDAO;
+    @Resource
+    private SysUserMapper sysUserMapper;
 
     @MockBean
     private SecurityProperties securityProperties;
@@ -47,8 +80,132 @@ public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
     private SysUserServiceImpl sysUserService;
     @MockBean
     private SysLoginLogServiceImpl sysLoginLogService;
-    @MockBean
-    private SysLoginUserRedisDAO sysLoginUserRedisDAO;
+
+    @Test
+    public void testCreateUserSession_success() {
+        // 准备参数
+        String userIp = randomString();
+        String userAgent = randomString();
+        LoginUser loginUser = randomPojo(LoginUser.class);
+        // mock
+        when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
+        // 调用
+        String sessionId = sysUserSessionService.createUserSession(loginUser, userIp, userAgent);
+        // 校验记录的属性是否正确
+        SysUserSessionDO sysUserSessionDO = sysUserSessionMapper.selectById(sessionId);
+        assertEquals(sysUserSessionDO.getId(), sessionId);
+        assertEquals(sysUserSessionDO.getUserId(), loginUser.getId());
+        assertEquals(sysUserSessionDO.getUserIp(), userIp);
+        assertEquals(sysUserSessionDO.getUserAgent(), userAgent);
+        assertEquals(sysUserSessionDO.getUsername(), loginUser.getUsername());
+        LoginUser redisLoginUser = sysLoginUserRedisDAO.get(sessionId);
+        AssertUtils.assertPojoEquals(redisLoginUser, loginUser, "username","password");
+    }
+
+    @Test
+    public void testCreateRefreshUserSession_success() {
+        // 准备参数
+        String sessionId = randomString();
+        String userIp = randomString();
+        String userAgent = randomString();
+        Long timeLong = randomLongId();
+        String userName = randomString();
+        Date date = randomDate();
+        LoginUser loginUser = randomPojo(LoginUser.class);
+        // mock
+        when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
+        loginUser.setUpdateTime(date);
+        sysLoginUserRedisDAO.set(sessionId, loginUser);
+        SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
+                .userId(loginUser.getId()).userIp(userIp).userAgent(userAgent).username(userName)
+                .sessionTimeout(addTime(Duration.ofMillis(timeLong)))
+                .build();
+        sysUserSessionMapper.insert(userSession);
+        SysUserSessionDO insertDO = sysUserSessionMapper.selectById(sessionId);
+        // 调用
+        sysUserSessionService.refreshUserSession(sessionId, loginUser);
+        // 校验记录 redis
+        LoginUser redisLoginUser = sysLoginUserRedisDAO.get(sessionId);
+        assertNotEquals(redisLoginUser.getUpdateTime(), date);
+        // 校验记录 SysUserSessionDO
+        SysUserSessionDO updateDO = sysUserSessionMapper.selectById(sessionId);
+        assertEquals(updateDO.getUsername(), loginUser.getUsername());
+        assertNotEquals(updateDO.getUpdateTime(), insertDO.getUpdateTime());
+        assertNotEquals(updateDO.getSessionTimeout(), addTime(Duration.ofMillis(timeLong)));
+    }
+
+    @Test
+    public void testDeleteUserSession_success() {
+        // 准备参数
+        String sessionId = randomString();
+        String userIp = randomString();
+        String userAgent = randomString();
+        Long timeLong = randomLongId();
+        LoginUser loginUser = randomPojo(LoginUser.class);
+        // mock 存入 Redis
+        when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1));
+        sysLoginUserRedisDAO.set(sessionId, loginUser);
+        // mock 存入 db
+        SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
+                .userId(loginUser.getId()).userIp(userIp).userAgent(userAgent).username(loginUser.getUsername())
+                .sessionTimeout(addTime(Duration.ofMillis(timeLong)))
+                .build();
+        sysUserSessionMapper.insert(userSession);
+        // 校验数据存在
+        assertNotNull(sysLoginUserRedisDAO.get(sessionId));
+        assertNotNull(sysUserSessionMapper.selectById(sessionId));
+        // 调用
+        sysUserSessionService.deleteUserSession(sessionId);
+        // 校验数据不存在了
+        assertNull(sysLoginUserRedisDAO.get(sessionId));
+        assertNull(sysUserSessionMapper.selectById(sessionId));
+    }
+
+    @Test
+    public void testGetUserSessionPage_success() {
+        // mock 数据
+        String userIp = randomString();
+        SysUserDO dbUser1 = randomPojo(SysUserDO.class, o -> {
+            o.setUsername("testUsername1");
+            o.setSex(randomEle(SysSexEnum.values()).getSEX());
+            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
+        });
+        SysUserDO dbUser2 = randomPojo(SysUserDO.class, o -> {
+            o.setUsername("testUsername2");
+            o.setSex(randomEle(SysSexEnum.values()).getSEX());
+            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
+        });
+        SysUserSessionDO dbSession = randomPojo(SysUserSessionDO.class, o -> {
+            o.setUserId(dbUser1.getId());
+            o.setUserIp(userIp);
+        });
+        sysUserMapper.insert(dbUser1);
+        sysUserMapper.insert(dbUser2);
+        sysUserSessionMapper.insert(dbSession);
+        sysUserSessionMapper.insert(ObjectUtils.clone(dbSession, o -> {
+            o.setId(randomString());
+            o.setUserId(dbUser2.getId());
+        }));
+        // 测试 userId 不匹配
+        sysUserSessionMapper.insert(ObjectUtils.clone(dbSession, o -> {
+            o.setId(randomString());
+            o.setUserId(123456l);
+        }));
+        // 测试 userIp 不匹配
+        sysUserSessionMapper.insert(ObjectUtils.clone(dbSession, o -> {
+            o.setId(randomString());
+            o.setUserIp("testUserIp");
+        }));
+        // 准备参数
+        SysUserSessionPageReqVO reqVo = new SysUserSessionPageReqVO();
+        reqVo.setUserIp(userIp);
+        // 调用
+        PageResult<SysUserSessionDO> pageResult = sysUserSessionService.getUserSessionPage(reqVo);
+        // 断言
+        assertEquals(3, pageResult.getTotal());
+        assertEquals(3, pageResult.getList().size());
+        assertPojoEquals(dbSession, pageResult.getList().get(0));
+    }
 
     @Test
     public void testClearSessionTimeout_success() throws Exception {