feat: 更新端口配置并添加监控支持

fix: 修复测试配置和依赖检查

perf: 优化雪花算法性能

refactor: 清理冗余代码和未使用的导入

style: 统一代码格式和注释

test: 添加单元测试和集成测试

ci: 更新CI配置和构建脚本

chore: 更新依赖和配置文件
This commit is contained in:
张翔
2026-03-13 08:50:19 +08:00
parent fe2e4110dd
commit 9f8bf041c3
169 changed files with 3565 additions and 132 deletions
@@ -0,0 +1,17 @@
package cn.novalon.manage.sys.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import io.r2dbc.spi.ConnectionFactory;
import org.mockito.Mockito;
@TestConfiguration
public class UnitTestConfig {
@Bean
@Primary
public ConnectionFactory testConnectionFactory() {
return Mockito.mock(ConnectionFactory.class);
}
}
@@ -1,9 +1,11 @@
package cn.novalon.manage.sys.core.service.impl;
import cn.novalon.manage.sys.core.domain.Dictionary;
import cn.novalon.manage.sys.core.exception.DictionaryAlreadyExistsException;
import cn.novalon.manage.sys.core.service.IDictionaryService;
import cn.novalon.manage.sys.infrastructure.db.dao.DictionaryDao;
import cn.novalon.manage.sys.infrastructure.db.converter.DictionaryConverter;
import cn.novalon.manage.sys.infrastructure.db.entity.DictionaryEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -13,9 +15,10 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.LocalDateTime;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class DictionaryServiceTest {
@@ -28,55 +31,212 @@ class DictionaryServiceTest {
private IDictionaryService service;
private Dictionary testDictionary;
private DictionaryEntity testEntity;
@BeforeEach
void setUp() {
service = new DictionaryService(dao, converter);
testDictionary = new Dictionary();
testDictionary.setId(1L);
testDictionary.setType("test_type");
testDictionary.setCode("test_code");
testDictionary.setName("Test Label");
testDictionary.setValue("test_value");
testDictionary.setSort(1);
testDictionary.setRemark("Test remark");
testDictionary.setCreatedAt(LocalDateTime.now());
testDictionary.setUpdatedAt(LocalDateTime.now());
testEntity = new DictionaryEntity();
testEntity.setId(1L);
testEntity.setType("test_type");
testEntity.setCode("test_code");
testEntity.setName("Test Label");
testEntity.setValue("test_value");
testEntity.setSort(1);
testEntity.setRemark("Test remark");
testEntity.setCreatedAt(LocalDateTime.now());
testEntity.setUpdatedAt(LocalDateTime.now());
}
@Test
void testFindAll() {
Dictionary dict1 = new Dictionary();
dict1.setId(1L);
dict1.setType("type1");
Dictionary dict2 = new Dictionary();
dict2.setId(2L);
dict2.setType("type2");
when(dao.findByDeletedAtIsNullOrderBySortAsc()).thenReturn(Flux.empty());
when(dao.findByDeletedAtIsNullOrderBySortAsc()).thenReturn(Flux.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testDictionary);
StepVerifier.create(service.findAll())
.expectNext(testDictionary)
.verifyComplete();
verify(dao).findByDeletedAtIsNullOrderBySortAsc();
verify(converter).toDomain(testEntity);
}
@Test
void testFindById() {
Dictionary dict = new Dictionary();
dict.setId(1L);
dict.setType("type1");
when(dao.findById(1L)).thenReturn(Mono.empty());
when(dao.findById(1L)).thenReturn(Mono.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testDictionary);
StepVerifier.create(service.findById(1L))
.expectNext(testDictionary)
.verifyComplete();
verify(dao).findById(1L);
verify(converter).toDomain(testEntity);
}
@Test
void testSave() {
Dictionary dict = new Dictionary();
dict.setId(1L);
dict.setType("type1");
void testFindById_NotFound() {
when(dao.findById(999L)).thenReturn(Mono.empty());
when(dao.save(any())).thenReturn(Mono.empty());
StepVerifier.create(service.save(dict))
StepVerifier.create(service.findById(999L))
.verifyComplete();
verify(dao).findById(999L);
verify(converter, never()).toDomain(any());
}
@Test
void testFindByType() {
when(dao.findByType("test_type")).thenReturn(Flux.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testDictionary);
StepVerifier.create(service.findByType("test_type"))
.expectNext(testDictionary)
.verifyComplete();
verify(dao).findByType("test_type");
verify(converter).toDomain(testEntity);
}
@Test
void testCheckTypeAndCodeExists_True() {
when(dao.findByTypeAndCode("test_type", "test_code")).thenReturn(Mono.just(testEntity));
StepVerifier.create(service.checkTypeAndCodeExists("test_type", "test_code"))
.expectNext(true)
.verifyComplete();
verify(dao).findByTypeAndCode("test_type", "test_code");
}
@Test
void testCheckTypeAndCodeExists_False() {
when(dao.findByTypeAndCode("test_type", "test_code")).thenReturn(Mono.empty());
StepVerifier.create(service.checkTypeAndCodeExists("test_type", "test_code"))
.expectNext(false)
.verifyComplete();
verify(dao).findByTypeAndCode("test_type", "test_code");
}
@Test
void testSave_NewDictionary_Success() {
Dictionary newDict = new Dictionary();
newDict.setType("test_type");
newDict.setCode("test_code");
newDict.setName("Test Label");
newDict.setValue("test_value");
when(dao.findByTypeAndCode("test_type", "test_code")).thenReturn(Mono.empty());
when(converter.toEntity(any())).thenReturn(testEntity);
when(dao.save(any())).thenReturn(Mono.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testDictionary);
StepVerifier.create(service.save(newDict))
.expectNextMatches(dict -> dict.getId() != null)
.verifyComplete();
verify(dao).findByTypeAndCode("test_type", "test_code");
verify(converter).toEntity(any());
verify(dao).save(any());
verify(converter).toDomain(testEntity);
}
@Test
void testSave_NewDictionary_AlreadyExists() {
Dictionary newDict = new Dictionary();
newDict.setType("test_type");
newDict.setCode("test_code");
when(dao.findByTypeAndCode("test_type", "test_code")).thenReturn(Mono.just(testEntity));
StepVerifier.create(service.save(newDict))
.expectError(DictionaryAlreadyExistsException.class)
.verify();
verify(dao).findByTypeAndCode("test_type", "test_code");
verify(converter, never()).toEntity(any());
verify(dao, never()).save(any());
}
@Test
void testSave_UpdateExistingDictionary() {
Dictionary existingDict = new Dictionary();
existingDict.setId(1L);
existingDict.setType("test_type");
existingDict.setCode("test_code");
when(converter.toEntity(existingDict)).thenReturn(testEntity);
when(dao.save(any())).thenReturn(Mono.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testDictionary);
StepVerifier.create(service.save(existingDict))
.expectNextMatches(dict -> dict.getId() == 1L)
.verifyComplete();
verify(dao, never()).findByTypeAndCode(anyString(), anyString());
verify(converter).toEntity(existingDict);
verify(dao).save(any());
verify(converter).toDomain(testEntity);
}
@Test
void testUpdate() {
Dictionary updateDict = new Dictionary();
updateDict.setName("Updated Name");
updateDict.setValue("updated_value");
updateDict.setRemark("Updated remark");
updateDict.setSort(2);
DictionaryEntity existingEntity = new DictionaryEntity();
existingEntity.setId(1L);
existingEntity.setType("test_type");
existingEntity.setCode("test_code");
existingEntity.setName("Old Name");
existingEntity.setValue("old_value");
existingEntity.setRemark("Old remark");
existingEntity.setSort(1);
when(dao.findById(1L)).thenReturn(Mono.just(existingEntity));
when(dao.save(any())).thenReturn(Mono.just(existingEntity));
when(converter.toDomain(existingEntity)).thenReturn(testDictionary);
StepVerifier.create(service.update(1L, updateDict))
.expectNextMatches(dict -> dict.getId() == 1L)
.verifyComplete();
verify(dao).findById(1L);
verify(dao).save(any());
verify(converter).toDomain(existingEntity);
}
@Test
void testUpdate_NotFound() {
Dictionary updateDict = new Dictionary();
updateDict.setName("Updated Name");
when(dao.findById(999L)).thenReturn(Mono.empty());
StepVerifier.create(service.update(999L, updateDict))
.verifyComplete();
verify(dao).findById(999L);
verify(dao, never()).save(any());
verify(converter, never()).toDomain(any());
}
@Test
@@ -0,0 +1,160 @@
package cn.novalon.manage.sys.core.service.impl;
import cn.novalon.manage.sys.core.domain.SysConfig;
import cn.novalon.manage.sys.infrastructure.db.converter.SysConfigConverter;
import cn.novalon.manage.sys.infrastructure.db.dao.SysConfigDao;
import cn.novalon.manage.sys.infrastructure.db.entity.SysConfigEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class SysConfigServiceTest {
@Mock
private SysConfigDao dao;
@Mock
private SysConfigConverter converter;
private SysConfigService configService;
private SysConfig testConfig;
private SysConfigEntity testEntity;
@BeforeEach
void setUp() {
configService = new SysConfigService(dao, converter);
testConfig = new SysConfig();
testConfig.setId(1L);
testConfig.setConfigKey("app.name");
testConfig.setConfigValue("Novalon Manage System");
testConfig.setConfigName("Application Name");
testConfig.setConfigType("system");
testEntity = new SysConfigEntity();
testEntity.setId(1L);
testEntity.setConfigKey("app.name");
testEntity.setConfigValue("Novalon Manage System");
testEntity.setConfigName("Application Name");
testEntity.setConfigType("system");
}
@Test
void testFindAll() {
when(dao.findByDeletedAtIsNull()).thenReturn(Flux.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testConfig);
StepVerifier.create(configService.findAll())
.expectNext(testConfig)
.verifyComplete();
verify(dao).findByDeletedAtIsNull();
verify(converter).toDomain(testEntity);
}
@Test
void testFindById() {
when(dao.findById(1L)).thenReturn(Mono.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testConfig);
StepVerifier.create(configService.findById(1L))
.expectNext(testConfig)
.verifyComplete();
verify(dao).findById(1L);
verify(converter).toDomain(testEntity);
}
@Test
void testFindById_NotFound() {
when(dao.findById(999L)).thenReturn(Mono.empty());
StepVerifier.create(configService.findById(999L))
.verifyComplete();
verify(dao).findById(999L);
verify(converter, never()).toDomain(any());
}
@Test
void testFindByConfigKey() {
when(dao.findByConfigKeyAndDeletedAtIsNull("app.name")).thenReturn(Mono.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testConfig);
StepVerifier.create(configService.findByConfigKey("app.name"))
.expectNext(testConfig)
.verifyComplete();
verify(dao).findByConfigKeyAndDeletedAtIsNull("app.name");
verify(converter).toDomain(testEntity);
}
@Test
void testFindByConfigKey_NotFound() {
when(dao.findByConfigKeyAndDeletedAtIsNull("nonexistent")).thenReturn(Mono.empty());
StepVerifier.create(configService.findByConfigKey("nonexistent"))
.verifyComplete();
verify(dao).findByConfigKeyAndDeletedAtIsNull("nonexistent");
verify(converter, never()).toDomain(any());
}
@Test
void testSave() {
when(converter.toEntity(testConfig)).thenReturn(testEntity);
when(dao.save(testEntity)).thenReturn(Mono.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testConfig);
StepVerifier.create(configService.save(testConfig))
.expectNext(testConfig)
.verifyComplete();
verify(converter).toEntity(testConfig);
verify(dao).save(testEntity);
verify(converter).toDomain(testEntity);
}
@Test
void testDeleteById() {
when(dao.deleteByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.empty());
StepVerifier.create(configService.deleteById(1L))
.verifyComplete();
verify(dao).deleteByIdAndDeletedAtIsNull(1L);
}
@Test
void testGetConfigValue() {
when(dao.findByConfigKeyAndDeletedAtIsNull("app.name")).thenReturn(Mono.just(testEntity));
when(converter.toDomain(testEntity)).thenReturn(testConfig);
StepVerifier.create(configService.getConfigValue("app.name"))
.expectNext("Novalon Manage System")
.verifyComplete();
verify(dao).findByConfigKeyAndDeletedAtIsNull("app.name");
verify(converter).toDomain(testEntity);
}
@Test
void testGetConfigValue_NotFound() {
when(dao.findByConfigKeyAndDeletedAtIsNull("nonexistent")).thenReturn(Mono.empty());
StepVerifier.create(configService.getConfigValue("nonexistent"))
.verifyComplete();
verify(dao).findByConfigKeyAndDeletedAtIsNull("nonexistent");
}
}
@@ -0,0 +1,206 @@
package cn.novalon.manage.sys.core.service.impl;
import cn.novalon.manage.sys.core.constants.StatusConstants;
import cn.novalon.manage.sys.core.domain.SysRole;
import cn.novalon.manage.sys.core.repository.ISysRoleRepository;
import cn.novalon.manage.sys.dto.request.PageRequest;
import cn.novalon.manage.sys.dto.response.PageResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.relational.core.query.Query;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.LocalDateTime;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class SysRoleServiceTest {
@Mock
private ISysRoleRepository roleRepository;
private SysRoleService roleService;
private SysRole testRole;
@BeforeEach
void setUp() {
roleService = new SysRoleService(roleRepository);
testRole = new SysRole();
testRole.setId(1L);
testRole.setRoleName("admin");
testRole.setRoleKey("admin");
testRole.setStatus(StatusConstants.ENABLED);
testRole.setCreatedAt(LocalDateTime.now());
testRole.setUpdatedAt(LocalDateTime.now());
}
@Test
void testFindById() {
when(roleRepository.findById(1L)).thenReturn(Mono.just(testRole));
StepVerifier.create(roleService.findById(1L))
.expectNext(testRole)
.verifyComplete();
verify(roleRepository).findById(1L);
}
@Test
void testFindAll() {
when(roleRepository.findAll()).thenReturn(Flux.just(testRole));
StepVerifier.create(roleService.findAll())
.expectNext(testRole)
.verifyComplete();
verify(roleRepository).findAll();
}
@Test
void testFindRolesByPage() {
PageRequest pageRequest = new PageRequest();
pageRequest.setPage(0);
pageRequest.setSize(10);
pageRequest.setKeyword("admin");
PageResponse<SysRole> pageResponse = new PageResponse<>();
pageResponse.setContent(List.of(testRole));
pageResponse.setTotalElements(1L);
when(roleRepository.findByQueryWithPagination(any(Query.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(roleService.findRolesByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(roleRepository).findByQueryWithPagination(any(Query.class), eq(pageRequest));
}
@Test
void testCount() {
when(roleRepository.count()).thenReturn(Mono.just(5L));
StepVerifier.create(roleService.count())
.expectNext(5L)
.verifyComplete();
verify(roleRepository).count();
}
@Test
void testCreateRole() {
SysRole newRole = new SysRole();
newRole.setRoleName("user");
newRole.setRoleKey("user");
when(roleRepository.save(any(SysRole.class))).thenReturn(Mono.just(testRole));
StepVerifier.create(roleService.createRole(newRole))
.expectNextMatches(role ->
role.getStatus().equals(StatusConstants.ENABLED) &&
role.getCreatedAt() != null)
.verifyComplete();
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testUpdateRole() {
SysRole updateRole = new SysRole();
updateRole.setId(1L);
updateRole.setRoleName("updated_admin");
when(roleRepository.save(any(SysRole.class))).thenReturn(Mono.just(testRole));
StepVerifier.create(roleService.updateRole(updateRole))
.expectNextMatches(role -> role.getUpdatedAt() != null)
.verifyComplete();
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testDeleteRole() {
when(roleRepository.deleteById(1L)).thenReturn(Mono.empty());
StepVerifier.create(roleService.deleteRole(1L))
.verifyComplete();
verify(roleRepository).deleteById(1L);
}
@Test
void testFindByRoleName() {
when(roleRepository.findByRoleName("admin")).thenReturn(Mono.just(testRole));
StepVerifier.create(roleService.findByRoleName("admin"))
.expectNext(testRole)
.verifyComplete();
verify(roleRepository).findByRoleName("admin");
}
@Test
void testExistsByRoleName_True() {
when(roleRepository.existsByRoleName("admin")).thenReturn(Mono.just(true));
StepVerifier.create(roleService.existsByRoleName("admin"))
.expectNext(true)
.verifyComplete();
verify(roleRepository).existsByRoleName("admin");
}
@Test
void testExistsByRoleName_False() {
when(roleRepository.existsByRoleName("nonexistent")).thenReturn(Mono.just(false));
StepVerifier.create(roleService.existsByRoleName("nonexistent"))
.expectNext(false)
.verifyComplete();
verify(roleRepository).existsByRoleName("nonexistent");
}
@Test
void testLogicalDeleteRole() {
when(roleRepository.findByIdIncludingDeleted(1L)).thenReturn(Mono.just(testRole));
when(roleRepository.updateRole(any(SysRole.class))).thenReturn(Mono.just(testRole));
StepVerifier.create(roleService.logicalDeleteRole(1L))
.expectNextMatches(role -> role.getDeletedAt() != null)
.verifyComplete();
verify(roleRepository).findByIdIncludingDeleted(1L);
verify(roleRepository).updateRole(any(SysRole.class));
}
@Test
void testRestoreRole() {
SysRole deletedRole = new SysRole();
deletedRole.setId(1L);
deletedRole.setDeletedAt(LocalDateTime.now());
when(roleRepository.findByIdIncludingDeleted(1L)).thenReturn(Mono.just(deletedRole));
when(roleRepository.updateRole(any(SysRole.class))).thenReturn(Mono.just(testRole));
StepVerifier.create(roleService.restoreRole(1L))
.expectNextMatches(role -> role.getDeletedAt() == null)
.verifyComplete();
verify(roleRepository).findByIdIncludingDeleted(1L);
verify(roleRepository).updateRole(any(SysRole.class));
}
}
@@ -0,0 +1,334 @@
package cn.novalon.manage.sys.core.service.impl;
import cn.novalon.manage.sys.core.constants.StatusConstants;
import cn.novalon.manage.sys.core.domain.SysUser;
import cn.novalon.manage.sys.core.repository.ISysUserRepository;
import cn.novalon.manage.sys.dto.request.PageRequest;
import cn.novalon.manage.sys.dto.response.PageResponse;
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.data.relational.core.query.Query;
import org.springframework.security.crypto.password.PasswordEncoder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.LocalDateTime;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class SysUserServiceTest {
@Mock
private ISysUserRepository userRepository;
@Mock
private PasswordEncoder passwordEncoder;
private SysUserService userService;
private SysUser testUser;
@BeforeEach
void setUp() {
userService = new SysUserService(userRepository, passwordEncoder);
testUser = new SysUser();
testUser.setId(1L);
testUser.setUsername("testuser");
testUser.setPassword("encoded_password");
testUser.setEmail("test@example.com");
testUser.setRoleId(1L);
testUser.setStatus(StatusConstants.ENABLED);
testUser.setCreatedAt(LocalDateTime.now());
testUser.setUpdatedAt(LocalDateTime.now());
}
@Test
void testFindById() {
when(userRepository.findById(1L)).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.findById(1L))
.expectNext(testUser)
.verifyComplete();
verify(userRepository).findById(1L);
}
@Test
void testFindAll() {
when(userRepository.findAll()).thenReturn(Flux.just(testUser));
StepVerifier.create(userService.findAll())
.expectNext(testUser)
.verifyComplete();
verify(userRepository).findAll();
}
@Test
void testFindAll_IncludeDeleted() {
when(userRepository.findAll()).thenReturn(Flux.just(testUser));
StepVerifier.create(userService.findAll(true))
.expectNext(testUser)
.verifyComplete();
verify(userRepository).findAll();
}
@Test
void testFindAll_ExcludeDeleted() {
when(userRepository.findByDeletedAtIsNull()).thenReturn(Flux.just(testUser));
StepVerifier.create(userService.findAll(false))
.expectNext(testUser)
.verifyComplete();
verify(userRepository).findByDeletedAtIsNull();
}
@Test
void testFindUsersByPage() {
PageRequest pageRequest = new PageRequest();
pageRequest.setPage(0);
pageRequest.setSize(10);
pageRequest.setKeyword("test");
PageResponse<SysUser> pageResponse = new PageResponse<>();
pageResponse.setContent(List.of(testUser));
pageResponse.setTotalElements(1L);
when(userRepository.findByQueryWithPagination(any(Query.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(userService.findUsersByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(userRepository).findByQueryWithPagination(any(Query.class), eq(pageRequest));
}
@Test
void testFindUsersByPage_NoKeyword() {
PageRequest pageRequest = new PageRequest();
pageRequest.setPage(0);
pageRequest.setSize(10);
PageResponse<SysUser> pageResponse = new PageResponse<>();
pageResponse.setContent(List.of(testUser));
pageResponse.setTotalElements(1L);
when(userRepository.findByQueryWithPagination(any(Query.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(userService.findUsersByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(userRepository).findByQueryWithPagination(any(Query.class), eq(pageRequest));
}
@Test
void testCount() {
when(userRepository.count()).thenReturn(Mono.just(10L));
StepVerifier.create(userService.count())
.expectNext(10L)
.verifyComplete();
verify(userRepository).count();
}
@Test
void testFindByUsername() {
when(userRepository.findByUsername("testuser")).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.findByUsername("testuser"))
.expectNext(testUser)
.verifyComplete();
verify(userRepository).findByUsername("testuser");
}
@Test
void testCreateUser() {
SysUser newUser = new SysUser();
newUser.setUsername("newuser");
newUser.setPassword("raw_password");
newUser.setEmail("new@example.com");
when(passwordEncoder.encode("raw_password")).thenReturn("encoded_password");
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.createUser(newUser))
.expectNextMatches(user ->
user.getPassword().equals("encoded_password") &&
user.getStatus().equals(StatusConstants.ENABLED) &&
user.getCreatedAt() != null)
.verifyComplete();
ArgumentCaptor<SysUser> userCaptor = ArgumentCaptor.forClass(SysUser.class);
verify(userRepository).save(userCaptor.capture());
verify(passwordEncoder).encode("raw_password");
}
@Test
void testUpdateUser() {
SysUser updateUser = new SysUser();
updateUser.setId(1L);
updateUser.setUsername("updated_user");
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.updateUser(updateUser))
.expectNextMatches(user -> user.getUpdatedAt() != null)
.verifyComplete();
ArgumentCaptor<SysUser> userCaptor = ArgumentCaptor.forClass(SysUser.class);
verify(userRepository).save(userCaptor.capture());
}
@Test
void testDeleteUser() {
when(userRepository.deleteById(1L)).thenReturn(Mono.empty());
StepVerifier.create(userService.deleteUser(1L))
.verifyComplete();
verify(userRepository).deleteById(1L);
}
@Test
void testChangePassword_Success() {
when(userRepository.findById(1L)).thenReturn(Mono.just(testUser));
when(passwordEncoder.matches("old_password", "encoded_password")).thenReturn(true);
when(passwordEncoder.encode("new_password")).thenReturn("new_encoded_password");
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.changePassword(1L, "old_password", "new_password"))
.expectNextMatches(user -> user.getPassword().equals("new_encoded_password"))
.verifyComplete();
verify(passwordEncoder).matches("old_password", "encoded_password");
verify(passwordEncoder).encode("new_password");
verify(userRepository).save(any(SysUser.class));
}
@Test
void testChangePassword_WrongOldPassword() {
when(userRepository.findById(1L)).thenReturn(Mono.just(testUser));
when(passwordEncoder.matches("wrong_password", "encoded_password")).thenReturn(false);
StepVerifier.create(userService.changePassword(1L, "wrong_password", "new_password"))
.expectError(RuntimeException.class)
.verify();
verify(passwordEncoder).matches("wrong_password", "encoded_password");
verify(passwordEncoder, never()).encode(anyString());
verify(userRepository, never()).save(any(SysUser.class));
}
@Test
void testExistsByUsername_True() {
when(userRepository.findByUsername("testuser")).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.existsByUsername("testuser"))
.expectNext(true)
.verifyComplete();
verify(userRepository).findByUsername("testuser");
}
@Test
void testExistsByUsername_False() {
when(userRepository.findByUsername("nonexistent")).thenReturn(Mono.empty());
StepVerifier.create(userService.existsByUsername("nonexistent"))
.expectNext(false)
.verifyComplete();
verify(userRepository).findByUsername("nonexistent");
}
@Test
void testExistsByEmail_True() {
when(userRepository.findByEmail("test@example.com")).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.existsByEmail("test@example.com"))
.expectNext(true)
.verifyComplete();
verify(userRepository).findByEmail("test@example.com");
}
@Test
void testExistsByEmail_False() {
when(userRepository.findByEmail("nonexistent@example.com")).thenReturn(Mono.empty());
StepVerifier.create(userService.existsByEmail("nonexistent@example.com"))
.expectNext(false)
.verifyComplete();
verify(userRepository).findByEmail("nonexistent@example.com");
}
@Test
void testLogicalDeleteUser() {
when(userRepository.findByIdIncludingDeleted(1L)).thenReturn(Mono.just(testUser));
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.logicalDeleteUser(1L))
.verifyComplete();
ArgumentCaptor<SysUser> userCaptor = ArgumentCaptor.forClass(SysUser.class);
verify(userRepository).save(userCaptor.capture());
assert userCaptor.getValue().getDeletedAt() != null : "DeletedAt should be set";
}
@Test
void testLogicalDeleteUsers() {
List<Long> ids = List.of(1L, 2L, 3L);
when(userRepository.logicalDeleteByIds(ids)).thenReturn(Mono.empty());
StepVerifier.create(userService.logicalDeleteUsers(ids))
.verifyComplete();
verify(userRepository).logicalDeleteByIds(ids);
}
@Test
void testRestoreUser() {
SysUser deletedUser = new SysUser();
deletedUser.setId(1L);
deletedUser.setDeletedAt(LocalDateTime.now());
when(userRepository.findByIdIncludingDeleted(1L)).thenReturn(Mono.just(deletedUser));
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(testUser));
StepVerifier.create(userService.restoreUser(1L))
.verifyComplete();
ArgumentCaptor<SysUser> userCaptor = ArgumentCaptor.forClass(SysUser.class);
verify(userRepository).save(userCaptor.capture());
}
@Test
void testRestoreUsers() {
List<Long> ids = List.of(1L, 2L, 3L);
when(userRepository.restoreByIds(ids)).thenReturn(Mono.empty());
StepVerifier.create(userService.restoreUsers(ids))
.verifyComplete();
verify(userRepository).restoreByIds(ids);
}
}
@@ -77,8 +77,10 @@ class DictionaryHandlerTest {
when(service.save(any())).thenReturn(Mono.just(dict));
Mono<ServerResponse> responseMono = handler.createDictionary(MockServerRequest.builder()
.build());
MockServerRequest request = MockServerRequest.builder()
.body(Mono.just(dict));
Mono<ServerResponse> responseMono = handler.createDictionary(request);
StepVerifier.create(responseMono)
.expectNextMatches(response -> response.statusCode().equals(HttpStatus.CREATED))
@@ -4,7 +4,6 @@ import cn.novalon.manage.sys.core.domain.Dictionary;
import cn.novalon.manage.sys.infrastructure.db.entity.DictionaryEntity;
import org.junit.jupiter.api.Test;
import org.mapstruct.factory.Mappers;
import java.util.Arrays;
import java.util.List;
@@ -12,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.*;
class DictionaryMapperTest {
private final DictionaryMapper mapper = DictionaryMapper.INSTANCE;
private final DictionaryMapper mapper = Mappers.getMapper(DictionaryMapper.class);
@Test
void testToDomain() {
@@ -13,7 +13,6 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -0,0 +1,75 @@
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 10 },
{ duration: '1m', target: 50 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:8080';
export default function () {
const responses = http.batch([
['GET', `${BASE_URL}/api/users/page?page=0&size=10`, null, { tags: { name: 'UsersList' } }],
['GET', `${BASE_URL}/api/roles/page?page=0&size=10`, null, { tags: { name: 'RolesList' } }],
]);
check(responses[0], {
'users status is 200': (r) => r.status === 200,
'users response time < 500ms': (r) => r.timings.duration < 500,
});
check(responses[1], {
'roles status is 200': (r) => r.status === 200,
'roles response time < 500ms': (r) => r.timings.duration < 500,
});
const singleUserRes = http.get(`${BASE_URL}/api/users/1`);
check(singleUserRes, {
'single user status is 200 or 404': (r) => r.status === 200 || r.status === 404,
'single user response time < 300ms': (r) => r.timings.duration < 300,
});
const healthRes = http.get(`${BASE_URL}/actuator/health`);
check(healthRes, {
'health check status is 200': (r) => r.status === 200,
'health check response time < 100ms': (r) => r.timings.duration < 100,
});
sleep(1);
}
export function handleSummary(data) {
return {
'stdout': textSummary(data, { indent: ' ', enableColors: true }),
'performance-report.json': JSON.stringify(data, null, 2),
};
}
function textSummary(data, options) {
const indent = options?.indent || '';
const colors = options?.enableColors || false;
let summary = `\n${indent}📊 Performance Test Summary\n`;
summary += `${indent}============================\n\n`;
summary += `${indent}⏱️ HTTP Metrics:\n`;
summary += `${indent} - Total Requests: ${data.metrics.http_reqs?.values?.count || 0}\n`;
summary += `${indent} - Request Duration (p95): ${data.metrics.http_req_duration?.values?.['p(95)']?.toFixed(2) || 0}ms\n`;
summary += `${indent} - Request Failed Rate: ${(data.metrics.http_req_failed?.values?.rate * 100)?.toFixed(2) || 0}%\n`;
summary += `\n${indent}📈 Iterations:\n`;
summary += `${indent} - Total: ${data.metrics.iterations?.values?.count || 0}\n`;
summary += `${indent} - Rate: ${data.metrics.iterations?.values?.rate?.toFixed(2) || 0}/s\n`;
summary += `\n${indent}⏰ Test Duration: ${data.state?.testRunDurationMs ? (data.state.testRunDurationMs / 1000).toFixed(2) : 0}s\n`;
return summary;
}