package com.example.erp.module.usr; import com.example.erp.common.exception.BizException; import com.example.erp.common.util.JwtUtil; import com.example.erp.module.usr.dto.LoginReqDTO; import com.example.erp.module.usr.entity.BrandEntity; import com.example.erp.module.usr.entity.UsrUserEntity; import com.example.erp.module.usr.mapper.BrandMapper; import com.example.erp.module.usr.mapper.UsrUserMapper; import com.example.erp.module.usr.service.impl.AuthServiceImpl; import com.example.erp.module.usr.vo.BrandVO; import com.example.erp.module.usr.vo.LoginVO; import io.jsonwebtoken.Claims; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import java.time.LocalDateTime; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class AuthServiceTest { @BeforeAll static void initMyBatisEntityCache() { TableInfoHelper.initTableInfo( new MapperBuilderAssistant(new MybatisConfiguration(), ""), UsrUserEntity.class); } @Mock private BrandMapper brandMapper; @Mock private UsrUserMapper userMapper; @Mock private JwtUtil jwtUtil; @Mock private BCryptPasswordEncoder passwordEncoder; @InjectMocks private AuthServiceImpl authService; private LoginReqDTO req; private BrandEntity brand; private UsrUserEntity user; @BeforeEach void setUp() { req = new LoginReqDTO(); req.setBrandNo("STD"); req.setUsername("admin"); req.setPassword("666666"); brand = new BrandEntity(); brand.setSId("b1"); brand.setSNo("STD"); brand.setSName("标准版"); user = new UsrUserEntity(); user.setSId("u1"); user.setSUsername("admin"); user.setSPasswordHash("$2a$10$hashed"); user.setSUserType("普通用户"); user.setSLanguage("中文"); user.setBIsDisabled(0); user.setILoginFailCount(0); user.setTLockUntil(null); } @Test void login_brandNotFound_throws40100() { when(brandMapper.selectOne(any())).thenReturn(null); BizException ex = assertThrows(BizException.class, () -> authService.login(req)); assertEquals(40100, ex.getCode()); } @Test void login_userNotFound_throws40100() { when(brandMapper.selectOne(any())).thenReturn(brand); when(userMapper.selectOne(any())).thenReturn(null); BizException ex = assertThrows(BizException.class, () -> authService.login(req)); assertEquals(40100, ex.getCode()); } @Test void login_accountDisabled_throws40101() { user.setBIsDisabled(1); when(brandMapper.selectOne(any())).thenReturn(brand); when(userMapper.selectOne(any())).thenReturn(user); BizException ex = assertThrows(BizException.class, () -> authService.login(req)); assertEquals(40101, ex.getCode()); } @Test void login_accountLocked_throws40102WithRemainingMinutes() { user.setTLockUntil(LocalDateTime.now().plusMinutes(20)); when(brandMapper.selectOne(any())).thenReturn(brand); when(userMapper.selectOne(any())).thenReturn(user); BizException ex = assertThrows(BizException.class, () -> authService.login(req)); assertEquals(40102, ex.getCode()); assertTrue(ex.getMessage().contains("分钟")); } @Test void login_wrongPassword_firstTime_throws40100AndIncrementsCount() { when(brandMapper.selectOne(any())).thenReturn(brand); when(userMapper.selectOne(any())).thenReturn(user); when(passwordEncoder.matches(anyString(), anyString())).thenReturn(false); BizException ex = assertThrows(BizException.class, () -> authService.login(req)); assertEquals(40100, ex.getCode()); verify(userMapper).update(isNull(), any()); } @Test void login_wrongPassword_5thTime_setsLockAndThrows40102() { user.setILoginFailCount(4); when(brandMapper.selectOne(any())).thenReturn(brand); when(userMapper.selectOne(any())).thenReturn(user); when(passwordEncoder.matches(anyString(), anyString())).thenReturn(false); BizException ex = assertThrows(BizException.class, () -> authService.login(req)); assertEquals(40102, ex.getCode()); verify(userMapper).update(isNull(), any()); } @Test void login_success_resetsCountAndReturnsTokens() { when(brandMapper.selectOne(any())).thenReturn(brand); when(userMapper.selectOne(any())).thenReturn(user); when(passwordEncoder.matches(anyString(), anyString())).thenReturn(true); when(jwtUtil.generateAccessToken(anyString(), anyString(), anyString(), anyString())).thenReturn("access-token"); when(jwtUtil.generateRefreshToken(anyString(), anyString())).thenReturn("refresh-token"); LoginVO result = authService.login(req); assertEquals("access-token", result.getAccessToken()); assertEquals("refresh-token", result.getRefreshToken()); assertEquals(86400L, result.getExpiresIn()); assertNotNull(result.getUserInfo()); assertEquals("u1", result.getUserInfo().getUserId()); verify(userMapper).update(isNull(), any()); } // ---- Task 6: refresh + getBrands tests ---- @Test void refresh_validRefreshToken_returnsNewAccessToken() { Claims claims = mock(Claims.class); when(claims.getSubject()).thenReturn("u1"); when(claims.get("brandId", String.class)).thenReturn("b1"); when(jwtUtil.parseRefreshToken("valid-refresh")).thenReturn(claims); when(userMapper.selectOne(any())).thenReturn(user); when(jwtUtil.generateAccessToken(anyString(), anyString(), anyString(), anyString())).thenReturn("new-access-token"); String newToken = authService.refresh("valid-refresh"); assertEquals("new-access-token", newToken); } @Test void refresh_lockedUser_throws40103() { Claims claims = mock(Claims.class); when(claims.getSubject()).thenReturn("u1"); when(claims.get("brandId", String.class)).thenReturn("b1"); when(jwtUtil.parseRefreshToken("valid-refresh")).thenReturn(claims); user.setTLockUntil(LocalDateTime.now().plusMinutes(25)); when(userMapper.selectOne(any())).thenReturn(user); BizException ex = assertThrows(BizException.class, () -> authService.refresh("valid-refresh")); assertEquals(40103, ex.getCode()); } @Test void refresh_invalidRefreshToken_throws40103() { when(jwtUtil.parseRefreshToken("bad-token")) .thenThrow(new BizException(40103, "Refresh Token 已失效,请重新登录")); BizException ex = assertThrows(BizException.class, () -> authService.refresh("bad-token")); assertEquals(40103, ex.getCode()); } @Test void getBrands_returnsListSortedByName() { BrandEntity b1 = new BrandEntity(); b1.setSNo("STD"); b1.setSName("标准版"); BrandEntity b2 = new BrandEntity(); b2.setSNo("ENT"); b2.setSName("企业版"); when(brandMapper.selectList(any())).thenReturn(List.of(b1, b2)); List result = authService.getBrands(); assertEquals(2, result.size()); assertEquals("标准版", result.get(0).getSName()); } }