feat: extend operation log service and repository with pagination support

This commit is contained in:
张翔
2026-03-18 22:34:43 +08:00
parent 157aee2ffc
commit 8a0cd64829
81 changed files with 8842 additions and 509 deletions
+28
View File
@@ -58,6 +58,34 @@
<artifactId>resilience4j-reactor</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -1,5 +1,7 @@
package cn.novalon.manage.sys.core.repository;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import cn.novalon.manage.sys.core.domain.OperationLog;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -24,6 +26,8 @@ public interface IOperationLogRepository {
Flux<OperationLog> findByUsername(String username);
Mono<PageResponse<OperationLog>> findOperationLogsByPage(PageRequest pageRequest);
Mono<Long> count();
Mono<Long> countByCreatedAtAfter(LocalDateTime dateTime);
@@ -1,5 +1,7 @@
package cn.novalon.manage.sys.core.service;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import cn.novalon.manage.sys.core.domain.OperationLog;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -13,7 +15,9 @@ import reactor.core.publisher.Mono;
public interface IOperationLogService {
Mono<OperationLog> save(OperationLog log);
Flux<OperationLog> findAll();
Mono<OperationLog> findById(Long id);
Flux<OperationLog> findByUsername(String username);
Mono<PageResponse<OperationLog>> findOperationLogsByPage(PageRequest pageRequest);
Mono<Long> count();
Mono<Long> countToday();
}
@@ -1,5 +1,7 @@
package cn.novalon.manage.sys.core.service.impl;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import cn.novalon.manage.sys.core.domain.OperationLog;
import cn.novalon.manage.sys.core.repository.IOperationLogRepository;
import cn.novalon.manage.sys.core.service.IOperationLogService;
@@ -35,11 +37,21 @@ public class OperationLogService implements IOperationLogService {
return logRepository.findAll();
}
@Override
public Mono<OperationLog> findById(Long id) {
return logRepository.findById(id);
}
@Override
public Flux<OperationLog> findByUsername(String username) {
return logRepository.findByUsername(username);
}
@Override
public Mono<PageResponse<OperationLog>> findOperationLogsByPage(PageRequest pageRequest) {
return logRepository.findOperationLogsByPage(pageRequest);
}
@Override
public Mono<Long> count() {
return logRepository.count();
@@ -0,0 +1,79 @@
package cn.novalon.manage.sys.handler.log;
import cn.novalon.manage.sys.core.domain.OperationLog;
import cn.novalon.manage.sys.core.service.IOperationLogService;
import cn.novalon.manage.common.dto.PageRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
/**
* 操作日志处理器
*
* 文件定义:处理操作日志相关的HTTP请求
* 涉及业务:操作日志查询、分页、统计
* 算法:使用WebFlux函数式编程模型处理响应式请求
*
* @author 张翔
* @date 2026-03-18
*/
@Component
@Tag(name = "操作日志", description = "操作日志相关操作")
public class OperationLogHandler {
private final IOperationLogService logService;
public OperationLogHandler(IOperationLogService logService) {
this.logService = logService;
}
@Operation(summary = "获取所有操作日志", description = "获取系统中所有操作日志列表")
public Mono<ServerResponse> getAllOperationLogs(ServerRequest request) {
return ServerResponse.ok()
.body(logService.findAll(), OperationLog.class);
}
@Operation(summary = "根据ID获取操作日志", description = "根据操作日志ID获取详细信息")
public Mono<ServerResponse> getOperationLogById(ServerRequest request) {
Long id = Long.valueOf(request.pathVariable("id"));
return logService.findById(id)
.flatMap(log -> ServerResponse.ok().bodyValue(log))
.switchIfEmpty(ServerResponse.notFound().build());
}
@Operation(summary = "分页获取操作日志", description = "根据分页参数获取操作日志列表")
public Mono<ServerResponse> getOperationLogsByPage(ServerRequest request) {
int page = Integer.parseInt(request.queryParam("page").orElse("0"));
int size = Integer.parseInt(request.queryParam("size").orElse("10"));
String sort = request.queryParam("sort").orElse("created_at");
String order = request.queryParam("order").orElse("desc");
String keyword = request.queryParam("keyword").orElse(null);
PageRequest pageRequest = new PageRequest();
pageRequest.setPage(page);
pageRequest.setSize(size);
pageRequest.setSort(sort);
pageRequest.setOrder(order);
pageRequest.setKeyword(keyword);
return logService.findOperationLogsByPage(pageRequest)
.flatMap(response -> ServerResponse.ok().bodyValue(response));
}
@Operation(summary = "获取操作日志总数", description = "获取系统中操作日志总数")
public Mono<ServerResponse> getOperationLogCount(ServerRequest request) {
return logService.count()
.flatMap(count -> ServerResponse.ok().bodyValue(count));
}
@Operation(summary = "创建操作日志", description = "手动创建操作日志")
public Mono<ServerResponse> createOperationLog(ServerRequest request) {
return request.bodyToMono(OperationLog.class)
.flatMap(logService::save)
.flatMap(log -> ServerResponse.status(HttpStatus.CREATED).bodyValue(log));
}
}
@@ -0,0 +1,2 @@
cn.novalon.manage.sys.config.SecurityConfig
cn.novalon.manage.sys.config.ExceptionLogConfig
@@ -1,68 +0,0 @@
server:
port: 8084
netty:
connection-timeout: 60s
idle-timeout: 300s
spring:
application:
name: novalon-manage-api
servlet:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 10MB
datasource:
url: jdbc:postgresql://localhost:55432/manage_system
username: postgres
password: postgres
driver-class-name: org.postgresql.Driver
r2dbc:
url: r2dbc:pool:postgresql://localhost:55432/manage_system
username: postgres
password: postgres
flyway:
enabled: true
url: jdbc:postgresql://localhost:55432/manage_system
user: postgres
password: postgres
locations: classpath:db/migration
baseline-on-migrate: true
jwt:
secret: novalon-manage-secret-key-change-in-production
expiration: 86400000
websocket:
enabled: true
heartbeat-interval: 30s
idle-timeout: 300s
max-text-message-buffer-size: 8192
max-binary-message-buffer-size: 8192
resilience4j:
ratelimiter:
instances:
apiRateLimiter:
limit-for-period: 100
limit-refresh-period: 1s
timeout-duration: 0
logging:
level:
cn.novalon.manage: DEBUG
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
access: read-only
prometheus:
metrics:
export:
enabled: true
@@ -1,209 +0,0 @@
-- Novalon管理系统数据库初始化脚本
-- 版本: V1
-- 描述: 创建所有核心表
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
phone VARCHAR(20),
role_id BIGINT,
status INTEGER DEFAULT 1,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 角色表
CREATE TABLE IF NOT EXISTS roles (
id BIGSERIAL PRIMARY KEY,
role_name VARCHAR(100) NOT NULL,
role_key VARCHAR(100) NOT NULL UNIQUE,
role_sort INTEGER DEFAULT 0,
status INTEGER DEFAULT 1,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 菜单表
CREATE TABLE IF NOT EXISTS menus (
id BIGSERIAL PRIMARY KEY,
menu_name VARCHAR(50) NOT NULL,
parent_id BIGINT DEFAULT 0,
order_num INTEGER DEFAULT 0,
menu_type VARCHAR(1) DEFAULT 'C',
perms VARCHAR(100),
component VARCHAR(200),
status INTEGER DEFAULT 1,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 字典类型表
CREATE TABLE IF NOT EXISTS sys_dict_type (
id BIGSERIAL PRIMARY KEY,
dict_name VARCHAR(100) NOT NULL,
dict_type VARCHAR(100) NOT NULL UNIQUE,
status VARCHAR(1) DEFAULT '0',
remark VARCHAR(500),
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 字典数据表
CREATE TABLE IF NOT EXISTS sys_dict_data (
id BIGSERIAL PRIMARY KEY,
dict_sort INTEGER DEFAULT 0,
dict_label VARCHAR(100) NOT NULL,
dict_value VARCHAR(100) NOT NULL,
dict_type VARCHAR(100) NOT NULL,
css_class VARCHAR(100),
list_class VARCHAR(100),
is_default VARCHAR(1) DEFAULT 'N',
status VARCHAR(1) DEFAULT '0',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 系统配置表
CREATE TABLE IF NOT EXISTS sys_config (
id BIGSERIAL PRIMARY KEY,
config_name VARCHAR(100) NOT NULL,
config_key VARCHAR(100) NOT NULL UNIQUE,
config_value VARCHAR(500) NOT NULL,
config_type VARCHAR(1) DEFAULT 'N',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 登录日志表
CREATE TABLE IF NOT EXISTS sys_login_log (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50),
ip VARCHAR(50),
location VARCHAR(255),
browser VARCHAR(50),
os VARCHAR(50),
status VARCHAR(1),
message VARCHAR(255),
login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 异常日志表
CREATE TABLE IF NOT EXISTS sys_exception_log (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50),
ip VARCHAR(50),
location VARCHAR(255),
browser VARCHAR(50),
os VARCHAR(50),
status VARCHAR(1),
message VARCHAR(255),
exception_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 系统公告表
CREATE TABLE IF NOT EXISTS sys_notice (
id BIGSERIAL PRIMARY KEY,
notice_title VARCHAR(50) NOT NULL,
notice_type VARCHAR(1) NOT NULL,
notice_content TEXT,
status VARCHAR(1) DEFAULT '0',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 用户消息表
CREATE TABLE IF NOT EXISTS sys_user_message (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
notice_id BIGINT,
message_title VARCHAR(255),
message_content TEXT,
is_read VARCHAR(1) DEFAULT '0',
read_time TIMESTAMP,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 文件管理表
CREATE TABLE IF NOT EXISTS sys_file (
id BIGSERIAL PRIMARY KEY,
file_name VARCHAR(255) NOT NULL,
file_path VARCHAR(500) NOT NULL,
file_size BIGINT,
file_type VARCHAR(100),
file_extension VARCHAR(10),
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- OAuth2客户端表
CREATE TABLE IF NOT EXISTS oauth2_client (
id BIGSERIAL PRIMARY KEY,
client_id VARCHAR(100) NOT NULL UNIQUE,
client_secret VARCHAR(255) NOT NULL,
client_name VARCHAR(100),
web_server_redirect_uri VARCHAR(500),
scope VARCHAR(500),
authorized_grant_types VARCHAR(500),
access_token_validity_seconds INTEGER,
refresh_token_validity_seconds INTEGER,
auto_approve VARCHAR(1) DEFAULT 'false',
enabled VARCHAR(1) DEFAULT 'true',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 插入初始管理员用户
INSERT INTO users (username, password, email, role_id, status, create_by, update_by)
VALUES ('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'admin@novalon.com', 1, 1, 'system', 'system')
ON CONFLICT (username) DO NOTHING;
-- 插入初始角色
INSERT INTO roles (role_name, role_key, role_sort, status, create_by, update_by)
VALUES ('超级管理员', 'admin', 1, 1, 'system', 'system')
ON CONFLICT (role_key) DO NOTHING;
-- 插入初始字典类型
INSERT INTO sys_dict_type (dict_name, dict_type, status, remark, create_by, update_by)
VALUES ('用户状态', 'user_status', '0', '用户状态列表', 'system', 'system')
ON CONFLICT (dict_type) DO NOTHING;
-- 插入初始字典数据
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, update_by)
VALUES
(1, '正常', '1', 'user_status', '0', 'system', 'system'),
(2, '停用', '0', 'user_status', '0', 'system', 'system')
ON CONFLICT DO NOTHING;
@@ -1,18 +0,0 @@
-- 创建字典表
CREATE TABLE IF NOT EXISTS sys_dictionary (
id BIGSERIAL PRIMARY KEY,
type VARCHAR(100) NOT NULL,
code VARCHAR(100) NOT NULL,
name VARCHAR(100) NOT NULL,
value VARCHAR(500),
remark VARCHAR(500),
sort INTEGER DEFAULT 0,
create_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 创建索引
CREATE INDEX IF NOT EXISTS idx_sys_dictionary_type ON sys_dictionary(type);
CREATE INDEX IF NOT EXISTS idx_sys_dictionary_type_code ON sys_dictionary(type, code);
@@ -0,0 +1,44 @@
package cn.novalon.manage.sys.config;
import cn.novalon.manage.common.handler.ExceptionLogService;
import cn.novalon.manage.sys.handler.ExceptionLogServiceImpl;
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 static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(MockitoExtension.class)
class ExceptionLogConfigTest {
@Mock
private ExceptionLogServiceImpl exceptionLogServiceImpl;
private ExceptionLogConfig exceptionLogConfig;
@BeforeEach
void setUp() {
exceptionLogConfig = new ExceptionLogConfig();
}
@Test
void testExceptionLogService() {
ExceptionLogService exceptionLogService = exceptionLogConfig.exceptionLogService(exceptionLogServiceImpl);
assertThat(exceptionLogService).isNotNull();
assertThat(exceptionLogService).isSameAs(exceptionLogServiceImpl);
}
@Test
void testExceptionLogService_DifferentInstance() {
ExceptionLogService exceptionLogService1 = exceptionLogConfig.exceptionLogService(exceptionLogServiceImpl);
ExceptionLogService exceptionLogService2 = exceptionLogConfig.exceptionLogService(exceptionLogServiceImpl);
assertThat(exceptionLogService1).isNotNull();
assertThat(exceptionLogService2).isNotNull();
assertThat(exceptionLogService1).isSameAs(exceptionLogServiceImpl);
assertThat(exceptionLogService2).isSameAs(exceptionLogServiceImpl);
}
}
@@ -0,0 +1,78 @@
package cn.novalon.manage.sys.config;
import cn.novalon.manage.sys.security.JwtAuthenticationFilter;
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.http.HttpMethod;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(MockitoExtension.class)
class SecurityConfigTest {
@Mock
private JwtAuthenticationFilter jwtAuthenticationFilter;
private SecurityConfig securityConfig;
@BeforeEach
void setUp() {
securityConfig = new SecurityConfig(jwtAuthenticationFilter);
}
@Test
void testPasswordEncoder() {
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
assertThat(passwordEncoder).isNotNull();
assertThat(passwordEncoder).isInstanceOf(org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.class);
String rawPassword = "testPassword123";
String encodedPassword = passwordEncoder.encode(rawPassword);
assertThat(encodedPassword).isNotNull();
assertThat(encodedPassword).isNotEqualTo(rawPassword);
assertThat(passwordEncoder.matches(rawPassword, encodedPassword)).isTrue();
assertThat(passwordEncoder.matches("wrongPassword", encodedPassword)).isFalse();
}
@Test
void testPasswordEncoder_SamePasswordDifferentHashes() {
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
String rawPassword = "testPassword123";
String hash1 = passwordEncoder.encode(rawPassword);
String hash2 = passwordEncoder.encode(rawPassword);
assertThat(hash1).isNotEqualTo(hash2);
assertThat(passwordEncoder.matches(rawPassword, hash1)).isTrue();
assertThat(passwordEncoder.matches(rawPassword, hash2)).isTrue();
}
@Test
void testPasswordEncoder_EmptyPassword() {
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
String encodedPassword = passwordEncoder.encode("");
assertThat(encodedPassword).isNotNull();
assertThat(passwordEncoder.matches("", encodedPassword)).isTrue();
}
@Test
void testPasswordEncoder_Strength() {
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
String rawPassword = "testPassword123";
String encodedPassword = passwordEncoder.encode(rawPassword);
assertThat(encodedPassword.length()).isGreaterThan(50);
assertThat(encodedPassword.startsWith("$2a$")).isTrue();
}
}
@@ -0,0 +1,211 @@
package cn.novalon.manage.sys.core.query;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class SysRoleQueryTest {
@Test
void testGettersAndSetters() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("admin");
query.setRoleKey("admin");
query.setStatus(1);
query.setKeyword("admin");
assertEquals("admin", query.getRoleName());
assertEquals("admin", query.getRoleKey());
assertEquals(1, query.getStatus());
assertEquals("admin", query.getKeyword());
}
@Test
void testSetNullValues() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName(null);
query.setRoleKey(null);
query.setStatus(null);
query.setKeyword(null);
assertNull(query.getRoleName());
assertNull(query.getRoleKey());
assertNull(query.getStatus());
assertNull(query.getKeyword());
}
@Test
void testSetEmptyStringValues() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("");
query.setRoleKey("");
query.setKeyword("");
assertEquals("", query.getRoleName());
assertEquals("", query.getRoleKey());
assertEquals("", query.getKeyword());
}
@Test
void testSetMultipleValues() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("user");
query.setRoleKey("user");
query.setStatus(0);
query.setKeyword("user");
assertEquals("user", query.getRoleName());
assertEquals("user", query.getRoleKey());
assertEquals(0, query.getStatus());
assertEquals("user", query.getKeyword());
}
@Test
void testSetLongRoleName() {
SysRoleQuery query = new SysRoleQuery();
String longRoleName = "a".repeat(100);
query.setRoleName(longRoleName);
assertEquals(longRoleName, query.getRoleName());
}
@Test
void testSetLongRoleKey() {
SysRoleQuery query = new SysRoleQuery();
String longRoleKey = "a".repeat(100);
query.setRoleKey(longRoleKey);
assertEquals(longRoleKey, query.getRoleKey());
}
@Test
void testSetLongKeyword() {
SysRoleQuery query = new SysRoleQuery();
String longKeyword = "a".repeat(100);
query.setKeyword(longKeyword);
assertEquals(longKeyword, query.getKeyword());
}
@Test
void testSetNegativeStatus() {
SysRoleQuery query = new SysRoleQuery();
query.setStatus(-1);
assertEquals(-1, query.getStatus());
}
@Test
void testSetZeroStatus() {
SysRoleQuery query = new SysRoleQuery();
query.setStatus(0);
assertEquals(0, query.getStatus());
}
@Test
void testSetPositiveStatus() {
SysRoleQuery query = new SysRoleQuery();
query.setStatus(1);
assertEquals(1, query.getStatus());
}
@Test
void testSetSpecialCharactersInRoleName() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("role@#$%");
assertEquals("role@#$%", query.getRoleName());
}
@Test
void testSetSpecialCharactersInRoleKey() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleKey("role@#$%");
assertEquals("role@#$%", query.getRoleKey());
}
@Test
void testSetSpecialCharactersInKeyword() {
SysRoleQuery query = new SysRoleQuery();
query.setKeyword("keyword@#$%");
assertEquals("keyword@#$%", query.getKeyword());
}
@Test
void testSetWhitespaceInValues() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName(" test role ");
query.setRoleKey(" test key ");
query.setKeyword(" test keyword ");
assertEquals(" test role ", query.getRoleName());
assertEquals(" test key ", query.getRoleKey());
assertEquals(" test keyword ", query.getKeyword());
}
@Test
void testSetUnicodeCharacters() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("角色名");
query.setRoleKey("角色键");
query.setKeyword("关键词");
assertEquals("角色名", query.getRoleName());
assertEquals("角色键", query.getRoleKey());
assertEquals("关键词", query.getKeyword());
}
@Test
void testSetNumbersInRoleName() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("role123");
assertEquals("role123", query.getRoleName());
}
@Test
void testSetNumbersInRoleKey() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleKey("role123");
assertEquals("role123", query.getRoleKey());
}
@Test
void testSetUnderscoreInValues() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("test_role");
query.setRoleKey("test_role");
query.setKeyword("test_keyword");
assertEquals("test_role", query.getRoleName());
assertEquals("test_role", query.getRoleKey());
assertEquals("test_keyword", query.getKeyword());
}
@Test
void testSetHyphenInValues() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("test-role");
query.setRoleKey("test-role");
query.setKeyword("test-keyword");
assertEquals("test-role", query.getRoleName());
assertEquals("test-role", query.getRoleKey());
assertEquals("test-keyword", query.getKeyword());
}
@Test
void testSetDotInValues() {
SysRoleQuery query = new SysRoleQuery();
query.setRoleName("test.role");
query.setRoleKey("test.role");
query.setKeyword("test.keyword");
assertEquals("test.role", query.getRoleName());
assertEquals("test.role", query.getRoleKey());
assertEquals("test.keyword", query.getKeyword());
}
}
@@ -0,0 +1,185 @@
package cn.novalon.manage.sys.core.query;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class SysUserQueryTest {
@Test
void testGettersAndSetters() {
SysUserQuery query = new SysUserQuery();
query.setUsername("testuser");
query.setEmail("test@example.com");
query.setRoleId(1L);
query.setStatus(1);
query.setKeyword("test");
assertEquals("testuser", query.getUsername());
assertEquals("test@example.com", query.getEmail());
assertEquals(1L, query.getRoleId());
assertEquals(1, query.getStatus());
assertEquals("test", query.getKeyword());
}
@Test
void testSetNullValues() {
SysUserQuery query = new SysUserQuery();
query.setUsername(null);
query.setEmail(null);
query.setRoleId(null);
query.setStatus(null);
query.setKeyword(null);
assertNull(query.getUsername());
assertNull(query.getEmail());
assertNull(query.getRoleId());
assertNull(query.getStatus());
assertNull(query.getKeyword());
}
@Test
void testSetEmptyStringValues() {
SysUserQuery query = new SysUserQuery();
query.setUsername("");
query.setEmail("");
query.setKeyword("");
assertEquals("", query.getUsername());
assertEquals("", query.getEmail());
assertEquals("", query.getKeyword());
}
@Test
void testSetMultipleValues() {
SysUserQuery query = new SysUserQuery();
query.setUsername("user1");
query.setEmail("user1@example.com");
query.setRoleId(2L);
query.setStatus(0);
query.setKeyword("user1");
assertEquals("user1", query.getUsername());
assertEquals("user1@example.com", query.getEmail());
assertEquals(2L, query.getRoleId());
assertEquals(0, query.getStatus());
assertEquals("user1", query.getKeyword());
}
@Test
void testSetLongUsername() {
SysUserQuery query = new SysUserQuery();
String longUsername = "a".repeat(100);
query.setUsername(longUsername);
assertEquals(longUsername, query.getUsername());
}
@Test
void testSetLongEmail() {
SysUserQuery query = new SysUserQuery();
String longEmail = "a".repeat(100) + "@example.com";
query.setEmail(longEmail);
assertEquals(longEmail, query.getEmail());
}
@Test
void testSetLongKeyword() {
SysUserQuery query = new SysUserQuery();
String longKeyword = "a".repeat(100);
query.setKeyword(longKeyword);
assertEquals(longKeyword, query.getKeyword());
}
@Test
void testSetNegativeRoleId() {
SysUserQuery query = new SysUserQuery();
query.setRoleId(-1L);
assertEquals(-1L, query.getRoleId());
}
@Test
void testSetZeroRoleId() {
SysUserQuery query = new SysUserQuery();
query.setRoleId(0L);
assertEquals(0L, query.getRoleId());
}
@Test
void testSetPositiveRoleId() {
SysUserQuery query = new SysUserQuery();
query.setRoleId(999L);
assertEquals(999L, query.getRoleId());
}
@Test
void testSetNegativeStatus() {
SysUserQuery query = new SysUserQuery();
query.setStatus(-1);
assertEquals(-1, query.getStatus());
}
@Test
void testSetZeroStatus() {
SysUserQuery query = new SysUserQuery();
query.setStatus(0);
assertEquals(0, query.getStatus());
}
@Test
void testSetPositiveStatus() {
SysUserQuery query = new SysUserQuery();
query.setStatus(1);
assertEquals(1, query.getStatus());
}
@Test
void testSetSpecialCharactersInUsername() {
SysUserQuery query = new SysUserQuery();
query.setUsername("user@#$%");
assertEquals("user@#$%", query.getUsername());
}
@Test
void testSetSpecialCharactersInEmail() {
SysUserQuery query = new SysUserQuery();
query.setEmail("user+test@example.com");
assertEquals("user+test@example.com", query.getEmail());
}
@Test
void testSetSpecialCharactersInKeyword() {
SysUserQuery query = new SysUserQuery();
query.setKeyword("keyword@#$%");
assertEquals("keyword@#$%", query.getKeyword());
}
@Test
void testSetWhitespaceInValues() {
SysUserQuery query = new SysUserQuery();
query.setUsername(" test user ");
query.setEmail(" test@example.com ");
query.setKeyword(" test keyword ");
assertEquals(" test user ", query.getUsername());
assertEquals(" test@example.com ", query.getEmail());
assertEquals(" test keyword ", query.getKeyword());
}
@Test
void testSetUnicodeCharacters() {
SysUserQuery query = new SysUserQuery();
query.setUsername("用户名");
query.setEmail("用户@example.com");
query.setKeyword("关键词");
assertEquals("用户名", query.getUsername());
assertEquals("用户@example.com", query.getEmail());
assertEquals("关键词", query.getKeyword());
}
}
@@ -185,6 +185,82 @@ class SysMenuServiceTest {
verify(menuRepository).findById(999L);
}
@Test
void testUpdateMenuWithCommand_WithPartialFields() {
SysMenu existingMenu = new SysMenu();
existingMenu.setId(1L);
existingMenu.setMenuName("系统管理");
existingMenu.setParentId(0L);
existingMenu.setOrderNum(1);
existingMenu.setMenuType("M");
existingMenu.setPerms("system");
existingMenu.setComponent("system");
existingMenu.setStatus(1);
SysMenu updatedMenu = new SysMenu();
updatedMenu.setId(1L);
updatedMenu.setMenuName("系统管理");
updatedMenu.setParentId(0L);
updatedMenu.setOrderNum(1);
updatedMenu.setMenuType("M");
updatedMenu.setPerms("system");
updatedMenu.setComponent("system");
updatedMenu.setStatus(1);
updatedMenu.setUpdatedAt(LocalDateTime.now());
when(menuRepository.findById(1L)).thenReturn(Mono.just(existingMenu));
when(menuRepository.save(any(SysMenu.class))).thenReturn(Mono.just(updatedMenu));
UpdateMenuCommand command = new UpdateMenuCommand(
1L, null, null, null, null, null, null, null
);
StepVerifier.create(menuService.updateMenu(command))
.expectNextMatches(menu -> menu.getUpdatedAt() != null)
.verifyComplete();
verify(menuRepository).findById(1L);
verify(menuRepository).save(any(SysMenu.class));
}
@Test
void testUpdateMenuWithCommand_WithAllFields() {
SysMenu existingMenu = new SysMenu();
existingMenu.setId(1L);
existingMenu.setMenuName("系统管理");
existingMenu.setParentId(0L);
existingMenu.setOrderNum(1);
existingMenu.setMenuType("M");
existingMenu.setPerms("system");
existingMenu.setComponent("system");
existingMenu.setStatus(1);
SysMenu updatedMenu = new SysMenu();
updatedMenu.setId(1L);
updatedMenu.setMenuName("系统管理(更新)");
updatedMenu.setParentId(2L);
updatedMenu.setOrderNum(2);
updatedMenu.setMenuType("C");
updatedMenu.setPerms("system:manage_updated");
updatedMenu.setComponent("system_updated");
updatedMenu.setStatus(0);
updatedMenu.setUpdatedAt(LocalDateTime.now());
when(menuRepository.findById(1L)).thenReturn(Mono.just(existingMenu));
when(menuRepository.save(any(SysMenu.class))).thenReturn(Mono.just(updatedMenu));
UpdateMenuCommand command = new UpdateMenuCommand(
1L, 2L, "系统管理(更新)", "C", 2, "system_updated", "system:manage_updated", 0
);
StepVerifier.create(menuService.updateMenu(command))
.expectNextMatches(menu -> menu.getUpdatedAt() != null)
.verifyComplete();
verify(menuRepository).findById(1L);
verify(menuRepository).save(any(SysMenu.class));
}
@Test
void testDeleteMenu() {
when(menuRepository.deleteById(1L)).thenReturn(Mono.empty());
@@ -220,4 +296,188 @@ class SysMenuServiceTest {
menu.getChildren().size() == 1)
.verifyComplete();
}
@Test
void testFindById_WhenMenuNotFound() {
when(menuRepository.findById(999L)).thenReturn(Mono.empty());
Mono<SysMenu> result = menuService.findById(999L);
StepVerifier.create(result)
.expectNextCount(0)
.verifyComplete();
verify(menuRepository).findById(999L);
}
@Test
void testFindAll_WhenNoMenusExist() {
when(menuRepository.findAll()).thenReturn(Flux.empty());
Flux<SysMenu> result = menuService.findAll();
StepVerifier.create(result)
.expectNextCount(0)
.verifyComplete();
verify(menuRepository).findAll();
}
@Test
void testFindByParentId_WhenNoChildrenExist() {
when(menuRepository.findByParentId(999L)).thenReturn(Flux.empty());
Flux<SysMenu> result = menuService.findByParentId(999L);
StepVerifier.create(result)
.expectNextCount(0)
.verifyComplete();
verify(menuRepository).findByParentId(999L);
}
@Test
void testCreateMenu_WithDefaultStatus() {
SysMenu newMenu = new SysMenu();
newMenu.setMenuName("新菜单");
newMenu.setParentId(0L);
newMenu.setOrderNum(1);
newMenu.setMenuType("M");
newMenu.setPerms("new:menu");
newMenu.setComponent("new");
newMenu.setStatus(null);
SysMenu savedMenu = new SysMenu();
savedMenu.setId(1L);
savedMenu.setMenuName("新菜单");
savedMenu.setParentId(0L);
savedMenu.setOrderNum(1);
savedMenu.setMenuType("M");
savedMenu.setPerms("new:menu");
savedMenu.setComponent("new");
savedMenu.setStatus(1);
savedMenu.setCreatedAt(LocalDateTime.now());
when(menuRepository.save(any(SysMenu.class))).thenReturn(Mono.just(savedMenu));
Mono<SysMenu> result = menuService.createMenu(newMenu);
StepVerifier.create(result)
.expectNextMatches(menu ->
menu.getStatus().equals(1) &&
menu.getCreatedAt() != null)
.verifyComplete();
verify(menuRepository).save(any(SysMenu.class));
}
@Test
void testCreateMenuWithCommand_WithDefaultStatus() {
CreateMenuCommand command = new CreateMenuCommand(
0L, "日志管理", "M", 3, "log", "log:manage", null
);
SysMenu createdMenu = new SysMenu();
createdMenu.setId(3L);
createdMenu.setMenuName("日志管理");
createdMenu.setParentId(0L);
createdMenu.setOrderNum(3);
createdMenu.setMenuType("M");
createdMenu.setPerms("log:manage");
createdMenu.setComponent("log");
createdMenu.setStatus(1);
createdMenu.setCreatedAt(LocalDateTime.now());
when(menuRepository.save(any(SysMenu.class))).thenReturn(Mono.just(createdMenu));
Mono<SysMenu> result = menuService.createMenu(command);
StepVerifier.create(result)
.expectNextMatches(menu ->
menu.getMenuName().equals("日志管理") &&
menu.getStatus().equals(1) &&
menu.getCreatedAt() != null)
.verifyComplete();
verify(menuRepository).save(any(SysMenu.class));
}
@Test
void testBuildMenuTree_WithEmptyTree() {
when(menuRepository.findAll()).thenReturn(Flux.empty());
Flux<SysMenu> result = menuService.buildMenuTree(menuService.findAll());
StepVerifier.create(result)
.expectNextCount(0)
.verifyComplete();
verify(menuRepository).findAll();
}
@Test
void testBuildMenuTree_WithMultiLevelTree() {
SysMenu rootMenu = new SysMenu();
rootMenu.setId(1L);
rootMenu.setMenuName("系统管理");
rootMenu.setParentId(0L);
SysMenu level1Menu = new SysMenu();
level1Menu.setId(2L);
level1Menu.setMenuName("用户管理");
level1Menu.setParentId(1L);
SysMenu level2Menu = new SysMenu();
level2Menu.setId(3L);
level2Menu.setMenuName("用户列表");
level2Menu.setParentId(2L);
when(menuRepository.findAll()).thenReturn(Flux.just(rootMenu, level1Menu, level2Menu));
Flux<SysMenu> result = menuService.buildMenuTree(menuService.findAll());
StepVerifier.create(result)
.expectNextMatches(menu ->
menu.getId().equals(1L) &&
menu.getChildren() != null &&
menu.getChildren().size() == 1 &&
menu.getChildren().get(0).getChildren() != null &&
menu.getChildren().get(0).getChildren().size() == 1)
.verifyComplete();
verify(menuRepository).findAll();
}
@Test
void testBuildMenuTree_WithMultipleRootMenus() {
SysMenu root1 = new SysMenu();
root1.setId(1L);
root1.setMenuName("系统管理");
root1.setParentId(0L);
SysMenu root2 = new SysMenu();
root2.setId(2L);
root2.setMenuName("监控管理");
root2.setParentId(0L);
SysMenu child1 = new SysMenu();
child1.setId(3L);
child1.setMenuName("用户管理");
child1.setParentId(1L);
SysMenu child2 = new SysMenu();
child2.setId(4L);
child2.setMenuName("性能监控");
child2.setParentId(2L);
when(menuRepository.findAll()).thenReturn(Flux.just(root1, root2, child1, child2));
Flux<SysMenu> result = menuService.buildMenuTree(menuService.findAll());
StepVerifier.create(result)
.expectNextCount(2)
.verifyComplete();
verify(menuRepository).findAll();
}
}
@@ -127,6 +127,118 @@ class SysRoleServiceTest {
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testFindRolesByPage_WithKeyword() {
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(cn.novalon.manage.sys.core.query.SysRoleQuery.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(roleService.findRolesByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(roleRepository).findByQueryWithPagination(any(cn.novalon.manage.sys.core.query.SysRoleQuery.class), eq(pageRequest));
}
@Test
void testFindRolesByPage_WithoutKeyword() {
PageRequest pageRequest = new PageRequest();
pageRequest.setPage(0);
pageRequest.setSize(10);
PageResponse<SysRole> pageResponse = new PageResponse<>();
pageResponse.setContent(List.of(testRole));
pageResponse.setTotalElements(1L);
when(roleRepository.findByQueryWithPagination(any(cn.novalon.manage.sys.core.query.SysRoleQuery.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(roleService.findRolesByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(roleRepository).findByQueryWithPagination(any(cn.novalon.manage.sys.core.query.SysRoleQuery.class), eq(pageRequest));
}
@Test
void testFindRolesByPage_WithEmptyKeyword() {
PageRequest pageRequest = new PageRequest();
pageRequest.setPage(0);
pageRequest.setSize(10);
pageRequest.setKeyword("");
PageResponse<SysRole> pageResponse = new PageResponse<>();
pageResponse.setContent(List.of(testRole));
pageResponse.setTotalElements(1L);
when(roleRepository.findByQueryWithPagination(any(cn.novalon.manage.sys.core.query.SysRoleQuery.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(roleService.findRolesByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(roleRepository).findByQueryWithPagination(any(cn.novalon.manage.sys.core.query.SysRoleQuery.class), eq(pageRequest));
}
@Test
void testUpdateRoleWithCommand_WithAllFields() {
SysRole existingRole = new SysRole();
existingRole.setId(1L);
existingRole.setRoleName("oldrole");
existingRole.setRoleKey("oldkey");
existingRole.setRoleSort(1);
existingRole.setStatus(StatusConstants.ENABLED);
when(roleRepository.findById(1L)).thenReturn(Mono.just(existingRole));
when(roleRepository.save(any(SysRole.class))).thenReturn(Mono.just(testRole));
cn.novalon.manage.sys.core.command.UpdateRoleCommand command =
new cn.novalon.manage.sys.core.command.UpdateRoleCommand(
1L, "newrole", "newkey", 2, StatusConstants.DISABLED
);
StepVerifier.create(roleService.updateRole(command))
.expectNextMatches(role -> role.getUpdatedAt() != null)
.verifyComplete();
verify(roleRepository).findById(1L);
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testUpdateRoleWithCommand_WithPartialFields() {
SysRole existingRole = new SysRole();
existingRole.setId(1L);
existingRole.setRoleName("oldrole");
existingRole.setRoleKey("oldkey");
existingRole.setRoleSort(1);
existingRole.setStatus(StatusConstants.ENABLED);
when(roleRepository.findById(1L)).thenReturn(Mono.just(existingRole));
when(roleRepository.save(any(SysRole.class))).thenReturn(Mono.just(testRole));
cn.novalon.manage.sys.core.command.UpdateRoleCommand command =
new cn.novalon.manage.sys.core.command.UpdateRoleCommand(
1L, null, null, null, null
);
StepVerifier.create(roleService.updateRole(command))
.expectNextMatches(role -> role.getUpdatedAt() != null)
.verifyComplete();
verify(roleRepository).findById(1L);
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testUpdateRole() {
SysRole updateRole = new SysRole();
@@ -218,4 +330,203 @@ class SysRoleServiceTest {
verify(roleRepository).findByIdIncludingDeleted(1L);
verify(roleRepository).updateRole(any(SysRole.class));
}
@Test
void testCreateRole_WithNullStatus() {
SysRole newRole = new SysRole();
newRole.setRoleName("user");
newRole.setRoleKey("user");
newRole.setStatus(null);
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 testCreateRole_WithExistingStatus() {
SysRole newRole = new SysRole();
newRole.setRoleName("user");
newRole.setRoleKey("user");
newRole.setStatus(StatusConstants.DISABLED);
SysRole savedRole = new SysRole();
savedRole.setId(1L);
savedRole.setRoleName("user");
savedRole.setRoleKey("user");
savedRole.setStatus(StatusConstants.DISABLED);
savedRole.setCreatedAt(LocalDateTime.now());
when(roleRepository.save(any(SysRole.class))).thenReturn(Mono.just(savedRole));
StepVerifier.create(roleService.createRole(newRole))
.expectNextMatches(role ->
role.getStatus().equals(StatusConstants.DISABLED) &&
role.getCreatedAt() != null)
.verifyComplete();
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testCreateRoleWithCommand_WithAllFields() {
cn.novalon.manage.sys.core.command.CreateRoleCommand command =
new cn.novalon.manage.sys.core.command.CreateRoleCommand(
"manager", "manager", 2, StatusConstants.ENABLED
);
SysRole savedRole = new SysRole();
savedRole.setId(1L);
savedRole.setRoleName("manager");
savedRole.setRoleKey("manager");
savedRole.setRoleSort(2);
savedRole.setStatus(StatusConstants.ENABLED);
savedRole.setCreatedAt(LocalDateTime.now());
when(roleRepository.save(any(SysRole.class))).thenReturn(Mono.just(savedRole));
StepVerifier.create(roleService.createRole(command))
.expectNextMatches(role ->
role.getRoleName().equals("manager") &&
role.getRoleKey().equals("manager") &&
role.getRoleSort() == 2 &&
role.getStatus().equals(StatusConstants.ENABLED) &&
role.getCreatedAt() != null)
.verifyComplete();
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testCreateRoleWithCommand_WithDefaultStatus() {
cn.novalon.manage.sys.core.command.CreateRoleCommand command =
new cn.novalon.manage.sys.core.command.CreateRoleCommand(
"viewer", "viewer", 3, null
);
SysRole savedRole = new SysRole();
savedRole.setId(1L);
savedRole.setRoleName("viewer");
savedRole.setRoleKey("viewer");
savedRole.setRoleSort(3);
savedRole.setStatus(StatusConstants.ENABLED);
savedRole.setCreatedAt(LocalDateTime.now());
when(roleRepository.save(any(SysRole.class))).thenReturn(Mono.just(savedRole));
StepVerifier.create(roleService.createRole(command))
.expectNextMatches(role ->
role.getRoleName().equals("viewer") &&
role.getRoleKey().equals("viewer") &&
role.getRoleSort() == 3 &&
role.getStatus().equals(StatusConstants.ENABLED) &&
role.getCreatedAt() != null)
.verifyComplete();
verify(roleRepository).save(any(SysRole.class));
}
@Test
void testUpdateRoleWithCommand_WhenRoleNotFound() {
cn.novalon.manage.sys.core.command.UpdateRoleCommand command =
new cn.novalon.manage.sys.core.command.UpdateRoleCommand(
999L, "newrole", "newkey", 2, StatusConstants.DISABLED
);
when(roleRepository.findById(999L)).thenReturn(Mono.empty());
StepVerifier.create(roleService.updateRole(command))
.expectError(RuntimeException.class)
.verify();
verify(roleRepository).findById(999L);
verify(roleRepository, never()).save(any(SysRole.class));
}
@Test
void testDeleteRole_WhenRoleNotFound() {
when(roleRepository.findById(1L)).thenReturn(Mono.empty());
StepVerifier.create(roleService.deleteRole(1L))
.expectComplete()
.verify();
verify(roleRepository).findById(1L);
verify(userService, never()).updateRoleIdToNullByRoleId(1L);
verify(roleRepository, never()).deleteById(1L);
}
@Test
void testLogicalDeleteRole_WhenRoleNotFound() {
when(roleRepository.findByIdIncludingDeleted(1L)).thenReturn(Mono.empty());
StepVerifier.create(roleService.logicalDeleteRole(1L))
.expectNextCount(0)
.verifyComplete();
verify(roleRepository).findByIdIncludingDeleted(1L);
verify(roleRepository, never()).updateRole(any(SysRole.class));
}
@Test
void testRestoreRole_WhenRoleNotFound() {
when(roleRepository.findByIdIncludingDeleted(1L)).thenReturn(Mono.empty());
StepVerifier.create(roleService.restoreRole(1L))
.expectNextCount(0)
.verifyComplete();
verify(roleRepository).findByIdIncludingDeleted(1L);
verify(roleRepository, never()).updateRole(any(SysRole.class));
}
@Test
void testFindById_WhenRoleNotFound() {
when(roleRepository.findById(999L)).thenReturn(Mono.empty());
StepVerifier.create(roleService.findById(999L))
.expectNextCount(0)
.verifyComplete();
verify(roleRepository).findById(999L);
}
@Test
void testFindByRoleName_WhenRoleNotFound() {
when(roleRepository.findByRoleName("nonexistent")).thenReturn(Mono.empty());
StepVerifier.create(roleService.findByRoleName("nonexistent"))
.expectNextCount(0)
.verifyComplete();
verify(roleRepository).findByRoleName("nonexistent");
}
@Test
void testFindAll_WhenNoRolesExist() {
when(roleRepository.findAll()).thenReturn(Flux.empty());
StepVerifier.create(roleService.findAll())
.expectNextCount(0)
.verifyComplete();
verify(roleRepository).findAll();
}
@Test
void testCount_WhenNoRolesExist() {
when(roleRepository.count()).thenReturn(Mono.just(0L));
StepVerifier.create(roleService.count())
.expectNext(0L)
.verifyComplete();
verify(roleRepository).count();
}
}
@@ -1,6 +1,7 @@
package cn.novalon.manage.sys.core.service.impl;
import cn.novalon.manage.common.util.StatusConstants;
import cn.novalon.manage.sys.core.command.UpdateUserCommand;
import cn.novalon.manage.sys.core.domain.SysUser;
import cn.novalon.manage.sys.core.query.SysUserQuery;
import cn.novalon.manage.sys.core.repository.ISysUserRepository;
@@ -188,22 +189,6 @@ class SysUserServiceTest {
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.findById(1L)).thenReturn(Mono.just(testUser));
@@ -339,4 +324,180 @@ class SysUserServiceTest {
verify(userRepository).restoreByIds(ids);
}
@Test
void testCreateUser_WithNullStatus() {
SysUser newUser = new SysUser();
newUser.setUsername("newuser");
newUser.setPassword("raw_password");
newUser.setEmail("new@example.com");
newUser.setStatus(null);
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();
verify(passwordEncoder).encode("raw_password");
verify(userRepository).save(any(SysUser.class));
}
@Test
void testCreateUser_WithExistingStatus() {
SysUser newUser = new SysUser();
newUser.setUsername("newuser");
newUser.setPassword("raw_password");
newUser.setEmail("new@example.com");
newUser.setStatus(StatusConstants.DISABLED);
SysUser savedUser = new SysUser();
savedUser.setId(1L);
savedUser.setUsername("newuser");
savedUser.setPassword("encoded_password");
savedUser.setEmail("new@example.com");
savedUser.setStatus(StatusConstants.DISABLED);
savedUser.setCreatedAt(LocalDateTime.now());
when(passwordEncoder.encode("raw_password")).thenReturn("encoded_password");
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(savedUser));
StepVerifier.create(userService.createUser(newUser))
.expectNextMatches(user ->
user.getPassword().equals("encoded_password") &&
user.getStatus().equals(StatusConstants.DISABLED) &&
user.getCreatedAt() != null)
.verifyComplete();
verify(passwordEncoder).encode("raw_password");
verify(userRepository).save(any(SysUser.class));
}
@Test
void testDeleteUser_UserNotFound() {
when(userRepository.findById(999L)).thenReturn(Mono.empty());
StepVerifier.create(userService.deleteUser(999L))
.expectError(RuntimeException.class)
.verify();
verify(userRepository).findById(999L);
verify(userRepository, never()).deleteById(anyLong());
}
@Test
void testFindUsersByPage_WithKeyword() {
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(SysUserQuery.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(userService.findUsersByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(userRepository).findByQueryWithPagination(any(SysUserQuery.class), eq(pageRequest));
}
@Test
void testFindUsersByPage_WithoutKeyword() {
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(SysUserQuery.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(userService.findUsersByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(userRepository).findByQueryWithPagination(any(SysUserQuery.class), eq(pageRequest));
}
@Test
void testFindUsersByPage_WithEmptyKeyword() {
PageRequest pageRequest = new PageRequest();
pageRequest.setPage(0);
pageRequest.setSize(10);
pageRequest.setKeyword("");
PageResponse<SysUser> pageResponse = new PageResponse<>();
pageResponse.setContent(List.of(testUser));
pageResponse.setTotalElements(1L);
when(userRepository.findByQueryWithPagination(any(SysUserQuery.class), eq(pageRequest)))
.thenReturn(Mono.just(pageResponse));
StepVerifier.create(userService.findUsersByPage(pageRequest))
.expectNextMatches(response -> response.getTotalElements() == 1L)
.verifyComplete();
verify(userRepository).findByQueryWithPagination(any(SysUserQuery.class), eq(pageRequest));
}
@Test
void testUpdateUserWithCommand_WithAllFields() {
SysUser existingUser = new SysUser();
existingUser.setId(1L);
existingUser.setUsername("olduser");
existingUser.setEmail("old@example.com");
existingUser.setRoleId(1L);
existingUser.setStatus(StatusConstants.ENABLED);
when(userRepository.findById(1L)).thenReturn(Mono.just(existingUser));
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(testUser));
cn.novalon.manage.sys.core.command.UpdateUserCommand command =
new cn.novalon.manage.sys.core.command.UpdateUserCommand(
1L, "newuser", "newpass", "new@example.com", 2L, StatusConstants.DISABLED
);
StepVerifier.create(userService.updateUser(command))
.expectNextMatches(user -> user.getUpdatedAt() != null)
.verifyComplete();
verify(userRepository).findById(1L);
verify(userRepository).save(any(SysUser.class));
}
@Test
void testUpdateUserWithCommand_WithPartialFields() {
SysUser existingUser = new SysUser();
existingUser.setId(1L);
existingUser.setUsername("olduser");
existingUser.setEmail("old@example.com");
existingUser.setRoleId(1L);
existingUser.setStatus(StatusConstants.ENABLED);
when(userRepository.findById(1L)).thenReturn(Mono.just(existingUser));
when(userRepository.save(any(SysUser.class))).thenReturn(Mono.just(testUser));
cn.novalon.manage.sys.core.command.UpdateUserCommand command =
new cn.novalon.manage.sys.core.command.UpdateUserCommand(
1L, null, null, null, null, null
);
StepVerifier.create(userService.updateUser(command))
.expectNextMatches(user -> user.getUpdatedAt() != null)
.verifyComplete();
verify(userRepository).findById(1L);
verify(userRepository).save(any(SysUser.class));
}
}
@@ -0,0 +1,181 @@
package cn.novalon.manage.sys.filter;
import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
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.http.HttpStatus;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.net.InetSocketAddress;
import java.time.Duration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class RateLimitFilterTest {
@Mock
private RateLimiterRegistry rateLimiterRegistry;
@Mock
private RateLimiter rateLimiter;
@Mock
private WebFilterChain webFilterChain;
private RateLimitFilter rateLimitFilter;
private MockServerWebExchange exchange;
@BeforeEach
void setUp() {
when(rateLimiterRegistry.rateLimiter("apiRateLimiter")).thenReturn(rateLimiter);
rateLimitFilter = new RateLimitFilter(rateLimiterRegistry);
exchange = MockServerWebExchange.from(
org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
.remoteAddress(new InetSocketAddress("192.168.1.1", 8080))
.build()
);
}
@Test
void testFilter_WithPermissionGranted() {
when(rateLimiter.acquirePermission()).thenReturn(true);
when(webFilterChain.filter(any())).thenReturn(Mono.empty());
Mono<Void> result = rateLimitFilter.filter(exchange, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any());
}
@Test
void testFilter_WithPermissionDenied() {
RateLimiterConfig config = RateLimiterConfig.custom()
.limitForPeriod(100)
.limitRefreshPeriod(Duration.ofSeconds(1))
.build();
when(rateLimiter.getRateLimiterConfig()).thenReturn(config);
when(rateLimiter.acquirePermission()).thenReturn(false);
Mono<Void> result = rateLimitFilter.filter(exchange, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.TOO_MANY_REQUESTS);
assertThat(exchange.getResponse().getHeaders().getFirst("X-RateLimit-Limit")).isEqualTo("100");
assertThat(exchange.getResponse().getHeaders().getFirst("X-RateLimit-Remaining")).isEqualTo("0");
assertThat(exchange.getResponse().getHeaders().getFirst("Retry-After")).isEqualTo("1");
}
@Test
void testFilter_WithXForwardedForHeader() {
when(rateLimiter.acquirePermission()).thenReturn(true);
when(webFilterChain.filter(any())).thenReturn(Mono.empty());
MockServerWebExchange exchangeWithHeader = MockServerWebExchange.from(
org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
.header("X-Forwarded-For", "10.0.0.1")
.build()
);
Mono<Void> result = rateLimitFilter.filter(exchangeWithHeader, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any());
}
@Test
void testFilter_WithXRealIPHeader() {
when(rateLimiter.acquirePermission()).thenReturn(true);
when(webFilterChain.filter(any())).thenReturn(Mono.empty());
MockServerWebExchange exchangeWithHeader = MockServerWebExchange.from(
org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
.header("X-Real-IP", "10.0.0.2")
.build()
);
Mono<Void> result = rateLimitFilter.filter(exchangeWithHeader, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any());
}
@Test
void testFilter_WithUnknownIP() {
when(rateLimiter.acquirePermission()).thenReturn(true);
when(webFilterChain.filter(any())).thenReturn(Mono.empty());
MockServerWebExchange exchangeWithUnknownIP = MockServerWebExchange.from(
org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
.header("X-Forwarded-For", "unknown")
.build()
);
Mono<Void> result = rateLimitFilter.filter(exchangeWithUnknownIP, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any());
}
@Test
void testFilter_WithEmptyIP() {
when(rateLimiter.acquirePermission()).thenReturn(true);
when(webFilterChain.filter(any())).thenReturn(Mono.empty());
MockServerWebExchange exchangeWithEmptyIP = MockServerWebExchange.from(
org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
.header("X-Forwarded-For", "")
.build()
);
Mono<Void> result = rateLimitFilter.filter(exchangeWithEmptyIP, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any());
}
@Test
void testFilter_WithNullRemoteAddress() {
when(rateLimiter.acquirePermission()).thenReturn(true);
when(webFilterChain.filter(any())).thenReturn(Mono.empty());
MockServerWebExchange exchangeWithNullAddress = MockServerWebExchange.from(
org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
.header("X-Forwarded-For", "unknown")
.header("X-Real-IP", "unknown")
.build()
);
Mono<Void> result = rateLimitFilter.filter(exchangeWithNullAddress, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any());
}
}
@@ -0,0 +1,120 @@
package cn.novalon.manage.sys.handler;
import cn.novalon.manage.sys.core.domain.SysExceptionLog;
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
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.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;
@ExtendWith(MockitoExtension.class)
class ExceptionLogServiceImplTest {
@Mock
private ISysExceptionLogService exceptionLogService;
private ExceptionLogServiceImpl exceptionLogServiceImpl;
@BeforeEach
void setUp() {
exceptionLogServiceImpl = new ExceptionLogServiceImpl(exceptionLogService);
}
@Test
void testLogException() {
SysExceptionLog savedLog = new SysExceptionLog();
savedLog.setId(1L);
savedLog.setTitle("测试异常");
savedLog.setExceptionName("TestException");
savedLog.setExceptionMsg("测试异常消息");
savedLog.setMethodName("testMethod");
savedLog.setIp("127.0.0.1");
savedLog.setExceptionStack("测试堆栈信息");
savedLog.setCreateTime(LocalDateTime.now());
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
StepVerifier.create(exceptionLogServiceImpl.logException(
"测试异常",
"TestException",
"测试异常消息",
"testMethod",
"127.0.0.1",
"测试堆栈信息"
))
.verifyComplete();
verify(exceptionLogService).save(any(SysExceptionLog.class));
}
@Test
void testLogException_WithEmptyFields() {
SysExceptionLog savedLog = new SysExceptionLog();
savedLog.setId(1L);
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
StepVerifier.create(exceptionLogServiceImpl.logException(
"",
"",
"",
"",
"",
""
))
.verifyComplete();
verify(exceptionLogService).save(any(SysExceptionLog.class));
}
@Test
void testLogException_WithNullFields() {
SysExceptionLog savedLog = new SysExceptionLog();
savedLog.setId(1L);
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
StepVerifier.create(exceptionLogServiceImpl.logException(
null,
null,
null,
null,
null,
null
))
.verifyComplete();
verify(exceptionLogService).save(any(SysExceptionLog.class));
}
@Test
void testLogException_WithLongStackTrace() {
String longStackTrace = "a".repeat(10000);
SysExceptionLog savedLog = new SysExceptionLog();
savedLog.setId(1L);
when(exceptionLogService.save(any(SysExceptionLog.class))).thenReturn(Mono.just(savedLog));
StepVerifier.create(exceptionLogServiceImpl.logException(
"测试异常",
"TestException",
"测试异常消息",
"testMethod",
"127.0.0.1",
longStackTrace
))
.verifyComplete();
verify(exceptionLogService).save(any(SysExceptionLog.class));
}
}
@@ -152,6 +152,29 @@ class SysLogHandlerTest {
verify(loginLogService).findLoginLogsByPage(any());
}
@Test
void testGetLoginLogsByPage_WithKeyword() {
PageResponse<SysLoginLog> pageResponse = new PageResponse<>();
pageResponse.setContent(java.util.Collections.singletonList(testLoginLog));
pageResponse.setTotalElements(1L);
when(loginLogService.findLoginLogsByPage(any())).thenReturn(Mono.just(pageResponse));
ServerRequest request = MockServerRequest.builder()
.queryParam("page", "0")
.queryParam("size", "10")
.queryParam("keyword", "test")
.build();
Mono<ServerResponse> response = logHandler.getLoginLogsByPage(request);
StepVerifier.create(response)
.expectNextMatches(serverResponse ->
serverResponse.statusCode() == HttpStatus.OK)
.verifyComplete();
verify(loginLogService).findLoginLogsByPage(any());
}
@Test
void testGetLoginLogCount() {
when(loginLogService.count()).thenReturn(Mono.just(100L));
@@ -261,6 +284,29 @@ class SysLogHandlerTest {
verify(exceptionLogService).findExceptionLogsByPage(any());
}
@Test
void testGetExceptionLogsByPage_WithKeyword() {
PageResponse<SysExceptionLog> pageResponse = new PageResponse<>();
pageResponse.setContent(java.util.Collections.singletonList(testExceptionLog));
pageResponse.setTotalElements(1L);
when(exceptionLogService.findExceptionLogsByPage(any())).thenReturn(Mono.just(pageResponse));
ServerRequest request = MockServerRequest.builder()
.queryParam("page", "0")
.queryParam("size", "10")
.queryParam("keyword", "test")
.build();
Mono<ServerResponse> response = logHandler.getExceptionLogsByPage(request);
StepVerifier.create(response)
.expectNextMatches(serverResponse ->
serverResponse.statusCode() == HttpStatus.OK)
.verifyComplete();
verify(exceptionLogService).findExceptionLogsByPage(any());
}
@Test
void testGetExceptionLogCount() {
when(exceptionLogService.count()).thenReturn(Mono.just(50L));
@@ -0,0 +1,235 @@
package cn.novalon.manage.sys.primitive;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class EmailTest {
@Test
void testOf_ValidEmail() {
Email email = Email.of("test@example.com");
assertEquals("test@example.com", email.getValue());
}
@Test
void testOf_NullEmail() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of(null)
);
assertEquals("Email is required", exception.getMessage());
}
@Test
void testOf_EmptyEmail() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of("")
);
assertEquals("Email is required", exception.getMessage());
}
@Test
void testOf_WhitespaceOnlyEmail() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of(" ")
);
assertEquals("Email is required", exception.getMessage());
}
@Test
void testOf_InvalidEmail_NoAtSymbol() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of("testexample.com")
);
assertEquals("Invalid email format", exception.getMessage());
}
@Test
void testOf_InvalidEmail_NoDomain() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of("test@")
);
assertEquals("Invalid email format", exception.getMessage());
}
@Test
void testOf_InvalidEmail_NoTLD() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of("test@example")
);
assertEquals("Invalid email format", exception.getMessage());
}
@Test
void testOf_InvalidEmail_ShortTLD() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of("test@example.c")
);
assertEquals("Invalid email format", exception.getMessage());
}
@Test
void testOf_ValidEmail_WithSubdomain() {
Email email = Email.of("test@mail.example.com");
assertEquals("test@mail.example.com", email.getValue());
}
@Test
void testOf_ValidEmail_WithPlus() {
Email email = Email.of("test+label@example.com");
assertEquals("test+label@example.com", email.getValue());
}
@Test
void testOf_ValidEmail_WithUnderscore() {
Email email = Email.of("test_user@example.com");
assertEquals("test_user@example.com", email.getValue());
}
@Test
void testOf_ValidEmail_WithHyphen() {
Email email = Email.of("test-user@example.com");
assertEquals("test-user@example.com", email.getValue());
}
@Test
void testOf_ValidEmail_WithDot() {
Email email = Email.of("test.user@example.com");
assertEquals("test.user@example.com", email.getValue());
}
@Test
void testOf_ValidEmail_WithNumbers() {
Email email = Email.of("test123@example.com");
assertEquals("test123@example.com", email.getValue());
}
@Test
void testOf_ValidEmail_WithMultipleDotsInDomain() {
Email email = Email.of("test@example.co.uk");
assertEquals("test@example.co.uk", email.getValue());
}
@Test
void testOf_ValidEmail_WithHyphenInDomain() {
Email email = Email.of("test@example-domain.com");
assertEquals("test@example-domain.com", email.getValue());
}
@Test
void testOfNullable_NullValue() {
Email email = Email.ofNullable(null);
assertNull(email);
}
@Test
void testOfNullable_EmptyValue() {
Email email = Email.ofNullable("");
assertNull(email);
}
@Test
void testOfNullable_WhitespaceValue() {
Email email = Email.ofNullable(" ");
assertNull(email);
}
@Test
void testOfNullable_ValidEmail() {
Email email = Email.ofNullable("test@example.com");
assertNotNull(email);
assertEquals("test@example.com", email.getValue());
}
@Test
void testEquals_SameValue() {
Email email1 = Email.of("test@example.com");
Email email2 = Email.of("test@example.com");
assertEquals(email1, email2);
}
@Test
void testEquals_DifferentValue() {
Email email1 = Email.of("test1@example.com");
Email email2 = Email.of("test2@example.com");
assertNotEquals(email1, email2);
}
@Test
void testEquals_SameObject() {
Email email = Email.of("test@example.com");
assertEquals(email, email);
}
@Test
void testEquals_Null() {
Email email = Email.of("test@example.com");
assertNotEquals(email, null);
}
@Test
void testEquals_DifferentClass() {
Email email = Email.of("test@example.com");
assertNotEquals(email, "test@example.com");
}
@Test
void testHashCode_SameValue() {
Email email1 = Email.of("test@example.com");
Email email2 = Email.of("test@example.com");
assertEquals(email1.hashCode(), email2.hashCode());
}
@Test
void testHashCode_DifferentValue() {
Email email1 = Email.of("test1@example.com");
Email email2 = Email.of("test2@example.com");
assertNotEquals(email1.hashCode(), email2.hashCode());
}
@Test
void testToString() {
Email email = Email.of("test@example.com");
assertEquals("test@example.com", email.toString());
}
@Test
void testOf_ValidEmail_WithLeadingTrailingWhitespace() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of(" test@example.com ")
);
assertEquals("Invalid email format", exception.getMessage());
}
@Test
void testOf_ValidEmail_WithNumbersInDomain() {
Email email = Email.of("test@123example.com");
assertEquals("test@123example.com", email.getValue());
}
@Test
void testOf_ValidEmail_WithMultipleAtSymbols() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of("test@@example.com")
);
assertEquals("Invalid email format", exception.getMessage());
}
@Test
void testOf_ValidEmail_WithSpecialCharsInLocalPart() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Email.of("test!#$%&'*+/=?^_`{|}~-@example.com")
);
assertEquals("Invalid email format", exception.getMessage());
}
}
@@ -0,0 +1,198 @@
package cn.novalon.manage.sys.primitive;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class PasswordTest {
@Test
void testOf_ValidPassword() {
Password password = Password.of("Test@123");
assertEquals("Test@123", password.getValue());
}
@Test
void testOf_NullPassword() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of(null)
);
assertEquals("Password is required", exception.getMessage());
}
@Test
void testOf_EmptyPassword() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("")
);
assertEquals("Password is required", exception.getMessage());
}
@Test
void testOf_WhitespaceOnlyPassword() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of(" ")
);
assertEquals("Password is required", exception.getMessage());
}
@Test
void testOf_TooShortPassword() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("Test@1")
);
assertEquals("Password must be at least 8 characters long", exception.getMessage());
}
@Test
void testOf_NoUppercase() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("test@123")
);
assertEquals("Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character", exception.getMessage());
}
@Test
void testOf_NoLowercase() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("TEST@123")
);
assertEquals("Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character", exception.getMessage());
}
@Test
void testOf_NoDigit() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("Test@abc")
);
assertEquals("Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character", exception.getMessage());
}
@Test
void testOf_NoSpecialCharacter() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("Test1234")
);
assertEquals("Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character", exception.getMessage());
}
@Test
void testOf_MinLengthBoundary() {
Password password = Password.of("Test@123");
assertEquals("Test@123", password.getValue());
}
@Test
void testOf_LongPassword() {
Password password = Password.of("VeryLongPassword@123456");
assertEquals("VeryLongPassword@123456", password.getValue());
}
@Test
void testOf_WithMultipleSpecialCharacters() {
Password password = Password.of("Test@#$%123");
assertEquals("Test@#$%123", password.getValue());
}
@Test
void testOf_WithUnderscore() {
Password password = Password.of("Test_123");
assertEquals("Test_123", password.getValue());
}
@Test
void testOf_WithHyphen() {
Password password = Password.of("Test-123");
assertEquals("Test-123", password.getValue());
}
@Test
void testEquals_SameValue() {
Password password1 = Password.of("Test@123");
Password password2 = Password.of("Test@123");
assertEquals(password1, password2);
}
@Test
void testEquals_DifferentValue() {
Password password1 = Password.of("Test@123");
Password password2 = Password.of("Test@456");
assertNotEquals(password1, password2);
}
@Test
void testEquals_SameObject() {
Password password = Password.of("Test@123");
assertEquals(password, password);
}
@Test
void testEquals_Null() {
Password password = Password.of("Test@123");
assertNotEquals(password, null);
}
@Test
void testEquals_DifferentClass() {
Password password = Password.of("Test@123");
assertNotEquals(password, "Test@123");
}
@Test
void testHashCode_SameValue() {
Password password1 = Password.of("Test@123");
Password password2 = Password.of("Test@123");
assertEquals(password1.hashCode(), password2.hashCode());
}
@Test
void testHashCode_DifferentValue() {
Password password1 = Password.of("Test@123");
Password password2 = Password.of("Test@456");
assertNotEquals(password1.hashCode(), password2.hashCode());
}
@Test
void testToString() {
Password password = Password.of("Test@123");
assertEquals("********", password.toString());
}
@Test
void testOf_WithSpacesInPassword() {
Password password = Password.of("Test @123");
assertEquals("Test @123", password.getValue());
}
@Test
void testOf_WithUnicodeCharacters() {
Password password = Password.of("Tëst@123");
assertEquals("Tëst@123", password.getValue());
}
@Test
void testOf_WithNumbersOnly() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("12345678")
);
assertEquals("Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character", exception.getMessage());
}
@Test
void testOf_WithLettersOnly() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Password.of("TestTest")
);
assertEquals("Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character", exception.getMessage());
}
}
@@ -0,0 +1,183 @@
package cn.novalon.manage.sys.primitive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
class UsernameTest {
@Test
void testOf_ValidUsername() {
Username username = Username.of("test_user123");
assertEquals("test_user123", username.getValue());
}
@Test
void testOf_NullUsername() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of(null)
);
assertEquals("Username is required", exception.getMessage());
}
@Test
void testOf_EmptyUsername() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of("")
);
assertEquals("Username is required", exception.getMessage());
}
@Test
void testOf_WhitespaceOnlyUsername() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of(" ")
);
assertEquals("Username is required", exception.getMessage());
}
@Test
void testOf_TooShortUsername() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of("ab")
);
assertEquals("Username must be at least 3 characters long", exception.getMessage());
}
@Test
void testOf_TooLongUsername() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of("a".repeat(51))
);
assertEquals("Username must be at most 50 characters long", exception.getMessage());
}
@Test
void testOf_WithSpecialCharacters() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of("user@name")
);
assertEquals("Username can only contain letters, numbers, and underscores", exception.getMessage());
}
@Test
void testOf_WithSpaces() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of("user name")
);
assertEquals("Username can only contain letters, numbers, and underscores", exception.getMessage());
}
@Test
void testOf_WithHyphens() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Username.of("user-name")
);
assertEquals("Username can only contain letters, numbers, and underscores", exception.getMessage());
}
@Test
void testOf_MinLengthBoundary() {
Username username = Username.of("abc");
assertEquals("abc", username.getValue());
}
@Test
void testOf_MaxLengthBoundary() {
Username username = Username.of("a".repeat(50));
assertEquals("a".repeat(50), username.getValue());
}
@Test
void testOf_WithLeadingTrailingWhitespace() {
Username username = Username.of(" test_user ");
assertEquals(" test_user ", username.getValue());
}
@Test
void testOf_OnlyLetters() {
Username username = Username.of("username");
assertEquals("username", username.getValue());
}
@Test
void testOf_OnlyNumbers() {
Username username = Username.of("123456");
assertEquals("123456", username.getValue());
}
@Test
void testOf_OnlyUnderscores() {
Username username = Username.of("___");
assertEquals("___", username.getValue());
}
@Test
void testEquals_SameValue() {
Username username1 = Username.of("testuser");
Username username2 = Username.of("testuser");
assertEquals(username1, username2);
}
@Test
void testEquals_DifferentValue() {
Username username1 = Username.of("testuser1");
Username username2 = Username.of("testuser2");
assertNotEquals(username1, username2);
}
@Test
void testEquals_SameObject() {
Username username = Username.of("testuser");
assertEquals(username, username);
}
@Test
void testEquals_Null() {
Username username = Username.of("testuser");
assertNotEquals(username, null);
}
@Test
void testEquals_DifferentClass() {
Username username = Username.of("testuser");
assertNotEquals(username, "testuser");
}
@Test
void testHashCode_SameValue() {
Username username1 = Username.of("testuser");
Username username2 = Username.of("testuser");
assertEquals(username1.hashCode(), username2.hashCode());
}
@Test
void testHashCode_DifferentValue() {
Username username1 = Username.of("testuser1");
Username username2 = Username.of("testuser2");
assertNotEquals(username1.hashCode(), username2.hashCode());
}
@Test
void testToString() {
Username username = Username.of("testuser");
assertEquals("testuser", username.toString());
}
@ParameterizedTest
@ValueSource(strings = {"user_123", "User_123", "USER_123", "123_user", "_user", "user_"})
void testOf_ValidFormats(String validUsername) {
Username username = Username.of(validUsername);
assertEquals(validUsername.trim(), username.getValue());
}
}
@@ -0,0 +1,135 @@
package cn.novalon.manage.sys.security;
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.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
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;
@ExtendWith(MockitoExtension.class)
class JwtAuthenticationFilterTest {
@Mock
private JwtTokenProvider jwtTokenProvider;
@Mock
private WebFilterChain webFilterChain;
private JwtAuthenticationFilter jwtAuthenticationFilter;
@BeforeEach
void setUp() {
jwtAuthenticationFilter = new JwtAuthenticationFilter(jwtTokenProvider);
}
@Test
void testFilter_WithValidToken() {
String validToken = "valid.jwt.token";
Long userId = 1L;
when(jwtTokenProvider.validateToken(validToken)).thenReturn(true);
when(jwtTokenProvider.getUserIdFromToken(validToken)).thenReturn(userId);
when(webFilterChain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
MockServerHttpRequest request = MockServerHttpRequest.get("/api/test")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
.build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
Mono<Void> result = jwtAuthenticationFilter.filter(exchange, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(jwtTokenProvider).validateToken(validToken);
verify(jwtTokenProvider).getUserIdFromToken(validToken);
verify(webFilterChain).filter(any(ServerWebExchange.class));
}
@Test
void testFilter_WithInvalidToken() {
String invalidToken = "invalid.jwt.token";
when(jwtTokenProvider.validateToken(invalidToken)).thenReturn(false);
when(webFilterChain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
MockServerHttpRequest request = MockServerHttpRequest.get("/api/test")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + invalidToken)
.build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
Mono<Void> result = jwtAuthenticationFilter.filter(exchange, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(jwtTokenProvider).validateToken(invalidToken);
verify(webFilterChain).filter(any(ServerWebExchange.class));
}
@Test
void testFilter_WithoutToken() {
when(webFilterChain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
MockServerHttpRequest request = MockServerHttpRequest.get("/api/test")
.build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
Mono<Void> result = jwtAuthenticationFilter.filter(exchange, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any(ServerWebExchange.class));
}
@Test
void testFilter_WithMalformedToken() {
String malformedToken = "Bearer";
when(webFilterChain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
MockServerHttpRequest request = MockServerHttpRequest.get("/api/test")
.header(HttpHeaders.AUTHORIZATION, malformedToken)
.build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
Mono<Void> result = jwtAuthenticationFilter.filter(exchange, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any(ServerWebExchange.class));
}
@Test
void testFilter_WithTokenWithoutBearerPrefix() {
String tokenWithoutBearer = "just.a.token";
when(webFilterChain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
MockServerHttpRequest request = MockServerHttpRequest.get("/api/test")
.header(HttpHeaders.AUTHORIZATION, tokenWithoutBearer)
.build();
ServerWebExchange exchange = MockServerWebExchange.from(request);
Mono<Void> result = jwtAuthenticationFilter.filter(exchange, webFilterChain);
StepVerifier.create(result)
.verifyComplete();
verify(webFilterChain).filter(any(ServerWebExchange.class));
}
}