refactor(backend): 重命名后端项目为 gym-manage-api,修改包名为 cn.novalon.gym.manage
This commit is contained in:
+97
@@ -0,0 +1,97 @@
|
||||
package cn.novalon.gym.manage.gateway.audit;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* AuditLogService单元测试
|
||||
*
|
||||
* 文件定义:测试审计日志服务的核心功能
|
||||
* 涉及业务:请求日志记录、响应日志记录、安全事件记录
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AuditLogServiceTest {
|
||||
|
||||
private AuditLogService auditLogService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
auditLogService = new AuditLogService();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogRequest() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.header("X-Request-Id", "test-request-123")
|
||||
.header("User-Agent", "TestAgent")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 8080))
|
||||
.build();
|
||||
|
||||
assertDoesNotThrow(() -> auditLogService.logRequest(request, "user123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogResponse() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.header("X-Request-Id", "test-request-456")
|
||||
.build();
|
||||
|
||||
auditLogService.logRequest(request, "user123");
|
||||
|
||||
assertDoesNotThrow(() -> auditLogService.logResponse("test-request-456", 200, 150));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogSecurityEvent() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/admin")
|
||||
.header("X-Request-Id", "test-request-789")
|
||||
.build();
|
||||
|
||||
auditLogService.logRequest(request, "user123");
|
||||
|
||||
assertDoesNotThrow(() ->
|
||||
auditLogService.logSecurityEvent("test-request-789", "UNAUTHORIZED_ACCESS", "User attempted to access admin resource"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogError() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.POST, "/api/data")
|
||||
.header("X-Request-Id", "test-request-error")
|
||||
.build();
|
||||
|
||||
auditLogService.logRequest(request, "user123");
|
||||
|
||||
assertDoesNotThrow(() ->
|
||||
auditLogService.logError("test-request-error", "INTERNAL_ERROR", "Database connection failed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogRequestWithoutRequestId() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/test")
|
||||
.remoteAddress(new InetSocketAddress("10.0.0.1", 8080))
|
||||
.build();
|
||||
|
||||
assertDoesNotThrow(() -> auditLogService.logRequest(request, "user456"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogResponseWithNonExistentRequestId() {
|
||||
assertDoesNotThrow(() -> auditLogService.logResponse("non-existent-id", 404, 50));
|
||||
}
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package cn.novalon.gym.manage.gateway.cache;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RequestCacheServiceTest {
|
||||
|
||||
private RequestCacheService cacheService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
cacheService = new RequestCacheService();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGet_CacheMiss() {
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
StepVerifier.create(cacheService.get(request))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPutAndGet_CacheHit() {
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
String response = "{\"data\":\"test\"}";
|
||||
cacheService.put(request, response);
|
||||
|
||||
StepVerifier.create(cacheService.get(request))
|
||||
.expectNext(response)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEvict() {
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
String response = "{\"data\":\"test\"}";
|
||||
cacheService.put(request, response);
|
||||
|
||||
cacheService.evict(request);
|
||||
|
||||
StepVerifier.create(cacheService.get(request))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEvictByPattern() {
|
||||
ServerHttpRequest request1 = MockServerHttpRequest
|
||||
.get("/api/test1")
|
||||
.build();
|
||||
ServerHttpRequest request2 = MockServerHttpRequest
|
||||
.get("/api/test2")
|
||||
.build();
|
||||
|
||||
cacheService.put(request1, "response1");
|
||||
cacheService.put(request2, "response2");
|
||||
|
||||
cacheService.evictByPattern("GET:/api/test.*");
|
||||
|
||||
assertEquals(0, cacheService.getCacheSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClear() {
|
||||
ServerHttpRequest request1 = MockServerHttpRequest
|
||||
.get("/api/test1")
|
||||
.build();
|
||||
ServerHttpRequest request2 = MockServerHttpRequest
|
||||
.get("/api/test2")
|
||||
.build();
|
||||
|
||||
cacheService.put(request1, "response1");
|
||||
cacheService.put(request2, "response2");
|
||||
|
||||
cacheService.clear();
|
||||
|
||||
assertEquals(0, cacheService.getCacheSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheDisabled() {
|
||||
cacheService.setCacheEnabled(false);
|
||||
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
cacheService.put(request, "response");
|
||||
|
||||
StepVerifier.create(cacheService.get(request))
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(0, cacheService.getCacheSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheStatistics() {
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
cacheService.put(request, "response");
|
||||
|
||||
StepVerifier.create(cacheService.get(request))
|
||||
.expectNext("response")
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(1, cacheService.getHitCount());
|
||||
assertEquals(0, cacheService.getMissCount());
|
||||
assertEquals(1.0, cacheService.getHitRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheMissStatistics() {
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
StepVerifier.create(cacheService.get(request))
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(0, cacheService.getHitCount());
|
||||
assertEquals(1, cacheService.getMissCount());
|
||||
assertEquals(0.0, cacheService.getHitRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMaxCacheSize() {
|
||||
cacheService.setMaxCacheSize(5);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test" + i)
|
||||
.build();
|
||||
cacheService.put(request, "response" + i);
|
||||
}
|
||||
|
||||
assertTrue(cacheService.getCacheSize() <= 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheWithQueryParams() {
|
||||
ServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test?param=value")
|
||||
.build();
|
||||
|
||||
String response = "{\"data\":\"test\"}";
|
||||
cacheService.put(request, response);
|
||||
|
||||
StepVerifier.create(cacheService.get(request))
|
||||
.expectNext(response)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheWithDifferentMethods() {
|
||||
ServerHttpRequest getRequest = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
ServerHttpRequest postRequest = MockServerHttpRequest
|
||||
.post("/api/test")
|
||||
.build();
|
||||
|
||||
cacheService.put(getRequest, "getResponse");
|
||||
cacheService.put(postRequest, "postResponse");
|
||||
|
||||
StepVerifier.create(cacheService.get(getRequest))
|
||||
.expectNext("getResponse")
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(cacheService.get(postRequest))
|
||||
.expectNext("postResponse")
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package cn.novalon.gym.manage.gateway.config;
|
||||
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
|
||||
import io.github.resilience4j.retry.Retry;
|
||||
import io.github.resilience4j.retry.RetryRegistry;
|
||||
import io.github.resilience4j.timelimiter.TimeLimiter;
|
||||
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* ResilienceConfig单元测试
|
||||
*
|
||||
* 文件定义:测试Resilience4j配置类的核心功能
|
||||
* 涉及业务:断路器、重试、超时配置
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ResilienceConfigTest {
|
||||
|
||||
@InjectMocks
|
||||
private ResilienceConfig resilienceConfig;
|
||||
|
||||
@Test
|
||||
void testCircuitBreakerRegistry_ShouldCreateRegistry() {
|
||||
ReflectionTestUtils.setField(resilienceConfig, "circuitBreakerEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "failureRateThreshold", 50.0f);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slowCallRateThreshold", 100.0f);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slowCallDurationThreshold", java.time.Duration.ofSeconds(2));
|
||||
ReflectionTestUtils.setField(resilienceConfig, "permittedNumberOfCallsInHalfOpenState", 10);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slidingWindowType", "COUNT_BASED");
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slidingWindowSize", 100);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "minimumNumberOfCalls", 10);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "waitDurationInOpenState", java.time.Duration.ofSeconds(10));
|
||||
|
||||
CircuitBreakerRegistry registry = resilienceConfig.circuitBreakerRegistry();
|
||||
|
||||
assertNotNull(registry);
|
||||
assertNotNull(registry.getConfiguration("default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGatewayCircuitBreaker_ShouldCreateCircuitBreaker() {
|
||||
ReflectionTestUtils.setField(resilienceConfig, "circuitBreakerEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "failureRateThreshold", 50.0f);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slowCallRateThreshold", 100.0f);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slowCallDurationThreshold", java.time.Duration.ofSeconds(2));
|
||||
ReflectionTestUtils.setField(resilienceConfig, "permittedNumberOfCallsInHalfOpenState", 10);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slidingWindowType", "COUNT_BASED");
|
||||
ReflectionTestUtils.setField(resilienceConfig, "slidingWindowSize", 100);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "minimumNumberOfCalls", 10);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "waitDurationInOpenState", java.time.Duration.ofSeconds(10));
|
||||
|
||||
CircuitBreakerRegistry registry = resilienceConfig.circuitBreakerRegistry();
|
||||
CircuitBreaker circuitBreaker = resilienceConfig.gatewayCircuitBreaker(registry);
|
||||
|
||||
assertNotNull(circuitBreaker);
|
||||
assertEquals("gateway", circuitBreaker.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRetryRegistry_ShouldCreateRegistry() {
|
||||
ReflectionTestUtils.setField(resilienceConfig, "retryEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "retryMaxAttempts", 3);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "retryWaitDuration", java.time.Duration.ofMillis(500));
|
||||
|
||||
RetryRegistry registry = resilienceConfig.retryRegistry();
|
||||
|
||||
assertNotNull(registry);
|
||||
assertNotNull(registry.getConfiguration("default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGatewayRetry_ShouldCreateRetry() {
|
||||
ReflectionTestUtils.setField(resilienceConfig, "retryEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "retryMaxAttempts", 3);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "retryWaitDuration", java.time.Duration.ofMillis(500));
|
||||
|
||||
RetryRegistry registry = resilienceConfig.retryRegistry();
|
||||
Retry retry = resilienceConfig.gatewayRetry(registry);
|
||||
|
||||
assertNotNull(retry);
|
||||
assertEquals("gateway", retry.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTimeLimiterRegistry_ShouldCreateRegistry() {
|
||||
ReflectionTestUtils.setField(resilienceConfig, "timeoutEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "timeoutDuration", java.time.Duration.ofSeconds(3));
|
||||
|
||||
TimeLimiterRegistry registry = resilienceConfig.timeLimiterRegistry();
|
||||
|
||||
assertNotNull(registry);
|
||||
assertNotNull(registry.getConfiguration("default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGatewayTimeLimiter_ShouldCreateTimeLimiter() {
|
||||
ReflectionTestUtils.setField(resilienceConfig, "timeoutEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceConfig, "timeoutDuration", java.time.Duration.ofSeconds(3));
|
||||
|
||||
TimeLimiterRegistry registry = resilienceConfig.timeLimiterRegistry();
|
||||
TimeLimiter timeLimiter = resilienceConfig.gatewayTimeLimiter(registry);
|
||||
|
||||
assertNotNull(timeLimiter);
|
||||
assertEquals("gateway", timeLimiter.getName());
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package cn.novalon.gym.manage.gateway.filter;
|
||||
|
||||
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.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class CompressionFilterTest {
|
||||
|
||||
private CompressionFilter compressionFilter;
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
compressionFilter = new CompressionFilter();
|
||||
compressionFilter.setCompressionEnabled(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WithGzipSupport() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.header("Accept-Encoding", "gzip, deflate")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
compressionFilter.filter(exchange, chain).block();
|
||||
|
||||
assertEquals("gzip", exchange.getResponse().getHeaders().getFirst("Content-Encoding"));
|
||||
verify(chain).filter(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WithDeflateSupport() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.header("Accept-Encoding", "deflate")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
compressionFilter.filter(exchange, chain).block();
|
||||
|
||||
assertEquals("deflate", exchange.getResponse().getHeaders().getFirst("Content-Encoding"));
|
||||
verify(chain).filter(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_NoAcceptEncoding() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
compressionFilter.filter(exchange, chain).block();
|
||||
|
||||
assertNull(exchange.getResponse().getHeaders().getFirst("Content-Encoding"));
|
||||
verify(chain).filter(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_CompressionDisabled() {
|
||||
compressionFilter.setCompressionEnabled(false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.header("Accept-Encoding", "gzip")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
compressionFilter.filter(exchange, chain).block();
|
||||
|
||||
assertNull(exchange.getResponse().getHeaders().getFirst("Content-Encoding"));
|
||||
verify(chain).filter(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_OptionsRequest() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.OPTIONS, "/api/test")
|
||||
.header("Accept-Encoding", "gzip")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
compressionFilter.filter(exchange, chain).block();
|
||||
|
||||
assertNull(exchange.getResponse().getHeaders().getFirst("Content-Encoding"));
|
||||
verify(chain).filter(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_VaryHeader() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.header("Accept-Encoding", "gzip")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
compressionFilter.filter(exchange, chain).block();
|
||||
|
||||
assertTrue(exchange.getResponse().getHeaders().get("Vary").contains("Accept-Encoding"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOrder() {
|
||||
assertEquals(Integer.MAX_VALUE - 100, compressionFilter.getOrder());
|
||||
}
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
package cn.novalon.gym.manage.gateway.filter;
|
||||
|
||||
import cn.novalon.gym.manage.gateway.util.JwtUtil;
|
||||
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.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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 reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.ArgumentCaptor.forClass;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class GatewayJwtAuthenticationFilterTest {
|
||||
|
||||
@Mock
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
private JwtAuthenticationFilter filter;
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
filter = new JwtAuthenticationFilter(jwtUtil);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_AllowAccess() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/login").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
verify(jwtUtil, never()).validateToken(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_Register() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.post("/api/auth/register").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
verify(jwtUtil, never()).validateToken(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_ActuatorHealth() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/health").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
verify(jwtUtil, never()).validateToken(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_ActuatorInfo() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/info").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
verify(jwtUtil, never()).validateToken(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_NoAuthHeader() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
verify(chain, never()).filter(any(ServerWebExchange.class));
|
||||
verify(jwtUtil, never()).validateToken(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_InvalidAuthHeader() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
|
||||
.header(HttpHeaders.AUTHORIZATION, "InvalidToken")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
verify(chain, never()).filter(any(ServerWebExchange.class));
|
||||
verify(jwtUtil, never()).validateToken(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_WithBearerPrefix() {
|
||||
String validToken = "valid.jwt.token";
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
when(jwtUtil.validateToken(validToken)).thenReturn(true);
|
||||
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
|
||||
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
|
||||
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(jwtUtil).validateToken(validToken);
|
||||
verify(jwtUtil).isTokenExpired(validToken);
|
||||
verify(jwtUtil).getUsernameFromToken(validToken);
|
||||
verify(jwtUtil).getUserIdFromToken(validToken);
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_InvalidToken() {
|
||||
String invalidToken = "invalid.jwt.token";
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + invalidToken)
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(jwtUtil.validateToken(invalidToken)).thenReturn(false);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
verify(jwtUtil).validateToken(invalidToken);
|
||||
verify(jwtUtil, never()).isTokenExpired(anyString());
|
||||
verify(chain, never()).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_ExpiredToken() {
|
||||
String expiredToken = "expired.jwt.token";
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + expiredToken)
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(jwtUtil.validateToken(expiredToken)).thenReturn(true);
|
||||
when(jwtUtil.isTokenExpired(expiredToken)).thenReturn(true);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
verify(jwtUtil).validateToken(expiredToken);
|
||||
verify(jwtUtil).isTokenExpired(expiredToken);
|
||||
verify(chain, never()).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_ValidToken() {
|
||||
String validToken = "valid.jwt.token";
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users/1")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
when(jwtUtil.validateToken(validToken)).thenReturn(true);
|
||||
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
|
||||
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
|
||||
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(jwtUtil).validateToken(validToken);
|
||||
verify(jwtUtil).isTokenExpired(validToken);
|
||||
verify(jwtUtil).getUsernameFromToken(validToken);
|
||||
verify(jwtUtil).getUserIdFromToken(validToken);
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHeadersAdded_ValidToken() {
|
||||
String validToken = "valid.jwt.token";
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
when(jwtUtil.validateToken(validToken)).thenReturn(true);
|
||||
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
|
||||
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
|
||||
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
var exchangeCaptor = forClass(ServerWebExchange.class);
|
||||
verify(chain).filter(exchangeCaptor.capture());
|
||||
ServerHttpRequest modifiedRequest = exchangeCaptor.getValue().getRequest();
|
||||
assert modifiedRequest.getHeaders().getFirst("X-User-Id").equals("1");
|
||||
assert modifiedRequest.getHeaders().getFirst("X-Username").equals("testuser");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedPath_AuthPath() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/logout").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
verify(jwtUtil, never()).validateToken(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActuatorPath_Metrics() {
|
||||
String validToken = "valid.jwt.token";
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/metrics")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
when(jwtUtil.validateToken(validToken)).thenReturn(true);
|
||||
when(jwtUtil.isTokenExpired(validToken)).thenReturn(false);
|
||||
when(jwtUtil.getUsernameFromToken(validToken)).thenReturn("testuser");
|
||||
when(jwtUtil.getUserIdFromToken(validToken)).thenReturn(1L);
|
||||
|
||||
Mono<Void> result = filter.apply(new JwtAuthenticationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(jwtUtil).validateToken(validToken);
|
||||
verify(jwtUtil).isTokenExpired(validToken);
|
||||
verify(jwtUtil).getUsernameFromToken(validToken);
|
||||
verify(jwtUtil).getUserIdFromToken(validToken);
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
package cn.novalon.gym.manage.gateway.filter;
|
||||
|
||||
import cn.novalon.gym.manage.gateway.config.RateLimitConfig;
|
||||
import io.github.resilience4j.ratelimiter.RateLimiter;
|
||||
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
|
||||
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.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RateLimitFilterTest {
|
||||
|
||||
@Mock
|
||||
private RateLimiter globalRateLimiter;
|
||||
|
||||
@Mock
|
||||
private RateLimiter ipRateLimiter;
|
||||
|
||||
@Mock
|
||||
private RateLimiter userRateLimiter;
|
||||
|
||||
@Mock
|
||||
private RateLimitConfig rateLimitConfig;
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
private RateLimitFilter rateLimitFilter;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
lenient().when(rateLimitConfig.isRateLimitEnabled()).thenReturn(true);
|
||||
|
||||
RateLimiterConfig config = RateLimiterConfig.custom()
|
||||
.limitForPeriod(100)
|
||||
.limitRefreshPeriod(Duration.ofSeconds(1))
|
||||
.timeoutDuration(Duration.ZERO)
|
||||
.build();
|
||||
|
||||
lenient().when(globalRateLimiter.getRateLimiterConfig()).thenReturn(config);
|
||||
lenient().when(ipRateLimiter.getRateLimiterConfig()).thenReturn(config);
|
||||
lenient().when(userRateLimiter.getRateLimiterConfig()).thenReturn(config);
|
||||
|
||||
rateLimitFilter = new RateLimitFilter(
|
||||
globalRateLimiter,
|
||||
ipRateLimiter,
|
||||
userRateLimiter,
|
||||
rateLimitConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenRateLimitDisabled_ShouldPassThrough() {
|
||||
when(rateLimitConfig.isRateLimitEnabled()).thenReturn(false);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verify(globalRateLimiter, never()).acquirePermission();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenGlobalRateLimitExceeded_ShouldReturn429() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(HttpStatus.TOO_MANY_REQUESTS, exchange.getResponse().getStatusCode());
|
||||
verify(chain, never()).filter(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenAllRateLimitsPass_ShouldContinueChain() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.header("X-User-Id", "user123")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verify(globalRateLimiter).acquirePermission();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WithoutUserId_ShouldSkipUserRateLimit() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetClientIp_FromXForwardedFor() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.header("X-Forwarded-For", "10.0.0.1, 192.168.1.1")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetClientIp_FromXRealIP() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.header("X-Real-IP", "10.0.0.2")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetClientIp_FromRemoteAddress() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.100", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRateLimitHeaders_WhenExceeded() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
|
||||
assertTrue(headers.containsKey("X-RateLimit-Limit"));
|
||||
assertTrue(headers.containsKey("X-RateLimit-Remaining"));
|
||||
assertTrue(headers.containsKey("Retry-After"));
|
||||
assertTrue(headers.containsKey("X-RateLimit-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCounters_WhenRequestsProcessed() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(1, rateLimitFilter.getTotalRequests());
|
||||
assertEquals(0, rateLimitFilter.getBlockedRequests());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCounters_WhenRequestsBlocked() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(1, rateLimitFilter.getTotalRequests());
|
||||
assertEquals(1, rateLimitFilter.getBlockedRequests());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testResetCounters() {
|
||||
when(globalRateLimiter.acquirePermission()).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("/api/test")
|
||||
.remoteAddress(new InetSocketAddress("192.168.1.1", 12345))
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
StepVerifier.create(rateLimitFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(1, rateLimitFilter.getTotalRequests());
|
||||
|
||||
rateLimitFilter.resetCounters();
|
||||
|
||||
assertEquals(0, rateLimitFilter.getTotalRequests());
|
||||
assertEquals(0, rateLimitFilter.getBlockedRequests());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOrder() {
|
||||
int order = rateLimitFilter.getOrder();
|
||||
assertEquals(Ordered.HIGHEST_PRECEDENCE + 100, order);
|
||||
}
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
package cn.novalon.gym.manage.gateway.filter;
|
||||
|
||||
import cn.novalon.gym.manage.gateway.service.PermissionService;
|
||||
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.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RbacAuthorizationFilterTest {
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
@Mock
|
||||
private PermissionService permissionService;
|
||||
|
||||
private RbacAuthorizationFilter filter;
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
filter = new RbacAuthorizationFilter(permissionService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_AllowAccess() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/login").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_Register() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.post("/api/auth/register").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_ActuatorHealth() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/health").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicPath_ActuatorInfo() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/info").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_NoUserId() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
verify(chain, never()).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_WithUserId() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
|
||||
.header("X-User-Id", "1")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(permissionService.hasPermission(eq(1L), eq("/api/users"), eq("GET"))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_PostMethod() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.post("/api/users")
|
||||
.header("X-User-Id", "1")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(permissionService.hasPermission(eq(1L), eq("/api/users"), eq("POST"))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_PutMethod() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.put("/api/users/1")
|
||||
.header("X-User-Id", "1")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(permissionService.hasPermission(eq(1L), eq("/api/users/1"), eq("PUT"))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_DeleteMethod() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.delete("/api/users/1")
|
||||
.header("X-User-Id", "1")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(permissionService.hasPermission(eq(1L), eq("/api/users/1"), eq("DELETE"))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProtectedPath_EmptyUserId() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
|
||||
.header("X-User-Id", "")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
verify(chain, never()).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedPath_AuthPath() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/logout").build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedPath_UserPath() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/users/profile")
|
||||
.header("X-User-Id", "1")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(permissionService.hasPermission(eq(1L), eq("/api/users/profile"), eq("GET"))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActuatorPath_Metrics() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/metrics")
|
||||
.header("X-User-Id", "1")
|
||||
.build();
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(permissionService.hasPermission(eq(1L), eq("/actuator/metrics"), eq("GET"))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(any(ServerWebExchange.class));
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
package cn.novalon.gym.manage.gateway.filter;
|
||||
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
|
||||
import io.github.resilience4j.retry.Retry;
|
||||
import io.github.resilience4j.retry.RetryConfig;
|
||||
import io.github.resilience4j.timelimiter.TimeLimiter;
|
||||
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
|
||||
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.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* ResilienceFilter单元测试
|
||||
*
|
||||
* 文件定义:测试容错过滤器的核心功能
|
||||
* 涉及业务:断路器、重试、超时、降级
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ResilienceFilterTest {
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
private CircuitBreaker circuitBreaker;
|
||||
private Retry retry;
|
||||
private TimeLimiter timeLimiter;
|
||||
private ResilienceFilter resilienceFilter;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
CircuitBreakerConfig cbConfig = CircuitBreakerConfig.custom()
|
||||
.failureRateThreshold(50)
|
||||
.slidingWindowSize(100)
|
||||
.minimumNumberOfCalls(10)
|
||||
.waitDurationInOpenState(Duration.ofSeconds(10))
|
||||
.build();
|
||||
|
||||
RetryConfig retryConfig = RetryConfig.custom()
|
||||
.maxAttempts(3)
|
||||
.waitDuration(Duration.ofMillis(500))
|
||||
.build();
|
||||
|
||||
TimeLimiterConfig tlConfig = TimeLimiterConfig.custom()
|
||||
.timeoutDuration(Duration.ofSeconds(3))
|
||||
.build();
|
||||
|
||||
circuitBreaker = CircuitBreaker.of("gateway", cbConfig);
|
||||
retry = Retry.of("gateway", retryConfig);
|
||||
timeLimiter = TimeLimiter.of("gateway", tlConfig);
|
||||
|
||||
resilienceFilter = new ResilienceFilter(circuitBreaker, retry, timeLimiter);
|
||||
|
||||
ReflectionTestUtils.setField(resilienceFilter, "resilienceEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceFilter, "circuitBreakerEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceFilter, "retryEnabled", true);
|
||||
ReflectionTestUtils.setField(resilienceFilter, "timeoutEnabled", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenResilienceDisabled_ShouldContinueChain() {
|
||||
ReflectionTestUtils.setField(resilienceFilter, "resilienceEnabled", false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(resilienceFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenAllPatternsEnabled_ShouldApplyResilience() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(resilienceFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenCircuitBreakerDisabled_ShouldSkipCircuitBreaker() {
|
||||
ReflectionTestUtils.setField(resilienceFilter, "circuitBreakerEnabled", false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(resilienceFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenRetryDisabled_ShouldSkipRetry() {
|
||||
ReflectionTestUtils.setField(resilienceFilter, "retryEnabled", false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(resilienceFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenTimeoutDisabled_ShouldSkipTimeout() {
|
||||
ReflectionTestUtils.setField(resilienceFilter, "timeoutEnabled", false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(resilienceFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenChainThrowsException_ShouldHandleFallback() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.error(new RuntimeException("Test error")));
|
||||
|
||||
StepVerifier.create(resilienceFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, exchange.getResponse().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOrder_ShouldReturnCorrectOrder() {
|
||||
int order = resilienceFilter.getOrder();
|
||||
|
||||
assertEquals(-2147483448, order);
|
||||
}
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
package cn.novalon.gym.manage.gateway.filter;
|
||||
|
||||
import cn.novalon.gym.manage.gateway.service.SignatureService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* SignatureFilter单元测试
|
||||
*
|
||||
* 文件定义:测试签名验证过滤器的核心功能
|
||||
* 涉及业务:签名验证、白名单过滤、错误响应
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SignatureFilterTest {
|
||||
|
||||
@Mock
|
||||
private SignatureService signatureService;
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
@InjectMocks
|
||||
private SignatureFilter signatureFilter;
|
||||
|
||||
private static final String TEST_SECRET = "TestSecretKey123";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(signatureFilter, "signatureEnabled", true);
|
||||
ReflectionTestUtils.setField(signatureFilter, "signatureSecret", TEST_SECRET);
|
||||
ReflectionTestUtils.setField(signatureFilter, "whitelistPaths", "/actuator/health,/actuator/info");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenSignatureDisabled_ShouldContinueChain() {
|
||||
ReflectionTestUtils.setField(signatureFilter, "signatureEnabled", false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verify(signatureService, never()).verifySignature(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenPathIsWhitelisted_ShouldContinueChain() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/actuator/health")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verify(signatureService, never()).verifySignature(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenSignatureValid_ShouldContinueChain() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.header("X-Signature", "valid-signature")
|
||||
.header("X-Timestamp", String.valueOf(System.currentTimeMillis()))
|
||||
.header("X-Nonce", "test-nonce")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(signatureService.verifySignature(any(), any())).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(signatureService).verifySignature(request, TEST_SECRET);
|
||||
verify(chain).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenSignatureInvalid_ShouldReturnUnauthorized() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.header("X-Signature", "invalid-signature")
|
||||
.header("X-Timestamp", String.valueOf(System.currentTimeMillis()))
|
||||
.header("X-Nonce", "test-nonce")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(signatureService.verifySignature(any(), any())).thenReturn(false);
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(signatureService).verifySignature(request, TEST_SECRET);
|
||||
verify(chain, never()).filter(any());
|
||||
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, exchange.getResponse().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenMissingSignatureHeaders_ShouldReturnUnauthorized() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(signatureService.verifySignature(any(), any())).thenReturn(false);
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(signatureService).verifySignature(request, TEST_SECRET);
|
||||
verify(chain, never()).filter(any());
|
||||
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, exchange.getResponse().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenMultipleWhitelistPaths_ShouldMatchAny() {
|
||||
MockServerHttpRequest request1 = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/actuator/health")
|
||||
.build();
|
||||
|
||||
MockServerHttpRequest request2 = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/actuator/info")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange1 = MockServerWebExchange.builder(request1).build();
|
||||
MockServerWebExchange exchange2 = MockServerWebExchange.builder(request2).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange1, chain))
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange2, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain, times(2)).filter(any());
|
||||
verify(signatureService, never()).verifySignature(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenPathStartsWithWhitelist_ShouldMatch() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/actuator/health/details")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verify(signatureService, never()).verifySignature(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOrder_ShouldReturnCorrectOrder() {
|
||||
int order = signatureFilter.getOrder();
|
||||
|
||||
assertEquals(-2147483498, order);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter_WhenSignatureEnabled_ShouldVerifySignature() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "/api/users")
|
||||
.header("X-Signature", "test-signature")
|
||||
.header("X-Timestamp", String.valueOf(System.currentTimeMillis()))
|
||||
.header("X-Nonce", "test-nonce")
|
||||
.build();
|
||||
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
when(signatureService.verifySignature(any(), any())).thenReturn(true);
|
||||
when(chain.filter(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(signatureFilter.filter(exchange, chain))
|
||||
.verifyComplete();
|
||||
|
||||
verify(signatureService).verifySignature(request, TEST_SECRET);
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package cn.novalon.gym.manage.gateway.health;
|
||||
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
|
||||
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
|
||||
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.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* GatewayHealthIndicator单元测试
|
||||
*
|
||||
* 文件定义:测试网关健康检查指示器的核心功能
|
||||
* 涉及业务:断路器健康检查、限流器健康检查
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class GatewayHealthIndicatorTest {
|
||||
|
||||
private CircuitBreakerRegistry circuitBreakerRegistry;
|
||||
private RateLimiterRegistry rateLimiterRegistry;
|
||||
private GatewayHealthIndicator healthIndicator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
CircuitBreakerConfig cbConfig = CircuitBreakerConfig.custom()
|
||||
.failureRateThreshold(50)
|
||||
.slidingWindowSize(100)
|
||||
.minimumNumberOfCalls(10)
|
||||
.waitDurationInOpenState(Duration.ofSeconds(10))
|
||||
.build();
|
||||
|
||||
RateLimiterConfig rlConfig = RateLimiterConfig.custom()
|
||||
.limitForPeriod(100)
|
||||
.limitRefreshPeriod(Duration.ofSeconds(1))
|
||||
.build();
|
||||
|
||||
circuitBreakerRegistry = CircuitBreakerRegistry.of(cbConfig);
|
||||
rateLimiterRegistry = RateLimiterRegistry.of(rlConfig);
|
||||
|
||||
healthIndicator = new GatewayHealthIndicator(circuitBreakerRegistry, rateLimiterRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHealth_WhenAllComponentsHealthy_ShouldReturnUp() {
|
||||
circuitBreakerRegistry.circuitBreaker("test-cb");
|
||||
rateLimiterRegistry.rateLimiter("test-rl");
|
||||
|
||||
Health health = healthIndicator.health();
|
||||
|
||||
assertEquals(Status.UP, health.getStatus());
|
||||
assertTrue(health.getDetails().containsKey("circuitBreakers"));
|
||||
assertTrue(health.getDetails().containsKey("rateLimiters"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHealth_WhenNoComponents_ShouldReturnUp() {
|
||||
Health health = healthIndicator.health();
|
||||
|
||||
assertEquals(Status.UP, health.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHealth_ShouldIncludeComponentDetails() {
|
||||
circuitBreakerRegistry.circuitBreaker("gateway");
|
||||
rateLimiterRegistry.rateLimiter("gateway");
|
||||
|
||||
Health health = healthIndicator.health();
|
||||
|
||||
assertTrue(health.getDetails().containsKey("circuitBreakers"));
|
||||
assertTrue(health.getDetails().containsKey("rateLimiters"));
|
||||
}
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
package cn.novalon.gym.manage.gateway.integration;
|
||||
|
||||
import cn.novalon.gym.manage.gateway.filter.RbacAuthorizationFilter;
|
||||
import cn.novalon.gym.manage.gateway.service.PermissionService;
|
||||
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.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RbacIntegrationTest {
|
||||
|
||||
@Mock
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
private RbacAuthorizationFilter filter;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
filter = new RbacAuthorizationFilter(permissionService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndToEnd_AdminUserFullAccess() {
|
||||
Long adminUserId = 1L;
|
||||
String adminPath = "/api/admin/users";
|
||||
String adminMethod = "GET";
|
||||
|
||||
when(permissionService.hasPermission(eq(adminUserId), eq(adminPath), eq(adminMethod))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get(adminPath)
|
||||
.header("X-User-Id", adminUserId.toString())
|
||||
.build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == null || exchange.getResponse().getStatusCode() == HttpStatus.OK;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndToEnd_RegularUserLimitedAccess() {
|
||||
Long regularUserId = 2L;
|
||||
String adminPath = "/api/admin/users";
|
||||
String userPath = "/api/users/profile";
|
||||
|
||||
when(permissionService.hasPermission(eq(regularUserId), eq(adminPath), eq("GET"))).thenReturn(false);
|
||||
when(permissionService.hasPermission(eq(regularUserId), eq(userPath), eq("GET"))).thenReturn(true);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest adminRequest = MockServerHttpRequest.get(adminPath)
|
||||
.header("X-User-Id", regularUserId.toString())
|
||||
.build();
|
||||
ServerWebExchange adminExchange = MockServerWebExchange.from(adminRequest);
|
||||
|
||||
Mono<Void> adminResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(adminExchange, chain);
|
||||
|
||||
StepVerifier.create(adminResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert adminExchange.getResponse().getStatusCode() == HttpStatus.FORBIDDEN;
|
||||
|
||||
MockServerHttpRequest userRequest = MockServerHttpRequest.get(userPath)
|
||||
.header("X-User-Id", regularUserId.toString())
|
||||
.build();
|
||||
ServerWebExchange userExchange = MockServerWebExchange.from(userRequest);
|
||||
|
||||
Mono<Void> userResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(userExchange, chain);
|
||||
|
||||
StepVerifier.create(userResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert userExchange.getResponse().getStatusCode() == null || userExchange.getResponse().getStatusCode() == HttpStatus.OK;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndToEnd_MultipleHttpMethods() {
|
||||
Long userId = 3L;
|
||||
String basePath = "/api/users";
|
||||
|
||||
when(permissionService.hasPermission(eq(userId), eq(basePath), eq("GET"))).thenReturn(true);
|
||||
when(permissionService.hasPermission(eq(userId), eq(basePath), eq("POST"))).thenReturn(true);
|
||||
when(permissionService.hasPermission(eq(userId), eq(basePath), eq("PUT"))).thenReturn(false);
|
||||
when(permissionService.hasPermission(eq(userId), eq(basePath), eq("DELETE"))).thenReturn(false);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
MockServerHttpRequest getRequest = MockServerHttpRequest.get(basePath)
|
||||
.header("X-User-Id", userId.toString())
|
||||
.build();
|
||||
ServerWebExchange getExchange = MockServerWebExchange.from(getRequest);
|
||||
|
||||
Mono<Void> getResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(getExchange, chain);
|
||||
|
||||
StepVerifier.create(getResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert getExchange.getResponse().getStatusCode() == null || getExchange.getResponse().getStatusCode() == HttpStatus.OK;
|
||||
|
||||
MockServerHttpRequest postRequest = MockServerHttpRequest.post(basePath)
|
||||
.header("X-User-Id", userId.toString())
|
||||
.build();
|
||||
ServerWebExchange postExchange = MockServerWebExchange.from(postRequest);
|
||||
|
||||
Mono<Void> postResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(postExchange, chain);
|
||||
|
||||
StepVerifier.create(postResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert postExchange.getResponse().getStatusCode() == null || postExchange.getResponse().getStatusCode() == HttpStatus.OK;
|
||||
|
||||
MockServerHttpRequest putRequest = MockServerHttpRequest.put(basePath)
|
||||
.header("X-User-Id", userId.toString())
|
||||
.build();
|
||||
ServerWebExchange putExchange = MockServerWebExchange.from(putRequest);
|
||||
|
||||
Mono<Void> putResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(putExchange, chain);
|
||||
|
||||
StepVerifier.create(putResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert putExchange.getResponse().getStatusCode() == HttpStatus.FORBIDDEN;
|
||||
|
||||
MockServerHttpRequest deleteRequest = MockServerHttpRequest.delete(basePath)
|
||||
.header("X-User-Id", userId.toString())
|
||||
.build();
|
||||
ServerWebExchange deleteExchange = MockServerWebExchange.from(deleteRequest);
|
||||
|
||||
Mono<Void> deleteResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(deleteExchange, chain);
|
||||
|
||||
StepVerifier.create(deleteResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert deleteExchange.getResponse().getStatusCode() == HttpStatus.FORBIDDEN;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndToEnd_PathMatchingScenarios() {
|
||||
Long userId = 4L;
|
||||
|
||||
when(permissionService.hasPermission(eq(userId), eq("/api/users"), eq("GET"))).thenReturn(true);
|
||||
when(permissionService.hasPermission(eq(userId), eq("/api/users/123"), eq("GET"))).thenReturn(true);
|
||||
when(permissionService.hasPermission(eq(userId), eq("/api/users/123/profile"), eq("GET"))).thenReturn(true);
|
||||
when(permissionService.hasPermission(eq(userId), eq("/api/admin"), eq("GET"))).thenReturn(false);
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
String[] allowedPaths = {"/api/users", "/api/users/123", "/api/users/123/profile"};
|
||||
for (String path : allowedPaths) {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get(path)
|
||||
.header("X-User-Id", userId.toString())
|
||||
.build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == null || exchange.getResponse().getStatusCode() == HttpStatus.OK;
|
||||
}
|
||||
|
||||
MockServerHttpRequest adminRequest = MockServerHttpRequest.get("/api/admin")
|
||||
.header("X-User-Id", userId.toString())
|
||||
.build();
|
||||
ServerWebExchange adminExchange = MockServerWebExchange.from(adminRequest);
|
||||
|
||||
Mono<Void> adminResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(adminExchange, chain);
|
||||
|
||||
StepVerifier.create(adminResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert adminExchange.getResponse().getStatusCode() == HttpStatus.FORBIDDEN;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndToEnd_PublicPathsBypass() {
|
||||
String[] publicPaths = {
|
||||
"/api/auth/login",
|
||||
"/api/auth/register",
|
||||
"/actuator/health",
|
||||
"/actuator/info"
|
||||
};
|
||||
|
||||
for (String path : publicPaths) {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get(path).build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(exchange, chain);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
assert exchange.getResponse().getStatusCode() == null || exchange.getResponse().getStatusCode() == HttpStatus.OK;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndToEnd_ErrorScenarios() {
|
||||
MockServerHttpRequest noHeaderRequest = MockServerHttpRequest.get("/api/users").build();
|
||||
ServerWebExchange noHeaderExchange = MockServerWebExchange.from(noHeaderRequest);
|
||||
|
||||
Mono<Void> noHeaderResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(noHeaderExchange, chain);
|
||||
|
||||
StepVerifier.create(noHeaderResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert noHeaderExchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
|
||||
MockServerHttpRequest invalidIdRequest = MockServerHttpRequest.get("/api/users")
|
||||
.header("X-User-Id", "invalid")
|
||||
.build();
|
||||
ServerWebExchange invalidIdExchange = MockServerWebExchange.from(invalidIdRequest);
|
||||
|
||||
Mono<Void> invalidIdResult = filter.apply(new RbacAuthorizationFilter.Config())
|
||||
.filter(invalidIdExchange, chain);
|
||||
|
||||
StepVerifier.create(invalidIdResult)
|
||||
.verifyComplete();
|
||||
|
||||
assert invalidIdExchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package cn.novalon.gym.manage.gateway.loadbalancer;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* CustomLoadBalancer单元测试
|
||||
*
|
||||
* 文件定义:测试自定义负载均衡器的核心功能
|
||||
* 涉及业务:轮询、随机、加权轮询、最少连接策略
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
class CustomLoadBalancerTest {
|
||||
|
||||
private CustomLoadBalancer loadBalancer;
|
||||
private List<ServiceInstance> instances;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
loadBalancer = new CustomLoadBalancer();
|
||||
|
||||
instances = Arrays.asList(
|
||||
createInstance("host1", 8080),
|
||||
createInstance("host2", 8080),
|
||||
createInstance("host3", 8080)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectByRoundRobin() {
|
||||
ServiceInstance instance1 = loadBalancer.selectInstance(
|
||||
instances,
|
||||
CustomLoadBalancer.LoadBalanceStrategy.ROUND_ROBIN);
|
||||
|
||||
ServiceInstance instance2 = loadBalancer.selectInstance(
|
||||
instances,
|
||||
CustomLoadBalancer.LoadBalanceStrategy.ROUND_ROBIN);
|
||||
|
||||
assertNotNull(instance1);
|
||||
assertNotNull(instance2);
|
||||
assertNotSame(instance1, instance2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectByRandom() {
|
||||
ServiceInstance instance = loadBalancer.selectInstance(
|
||||
instances,
|
||||
CustomLoadBalancer.LoadBalanceStrategy.RANDOM);
|
||||
|
||||
assertNotNull(instance);
|
||||
assertTrue(instances.contains(instance));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectByWeightedRoundRobin() {
|
||||
ServiceInstance instance = loadBalancer.selectInstance(
|
||||
instances,
|
||||
CustomLoadBalancer.LoadBalanceStrategy.WEIGHTED_ROUND_ROBIN);
|
||||
|
||||
assertNotNull(instance);
|
||||
assertTrue(instances.contains(instance));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectByLeastConnections() {
|
||||
ServiceInstance instance = loadBalancer.selectInstance(
|
||||
instances,
|
||||
CustomLoadBalancer.LoadBalanceStrategy.LEAST_CONNECTIONS);
|
||||
|
||||
assertNotNull(instance);
|
||||
assertTrue(instances.contains(instance));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectInstance_EmptyList() {
|
||||
ServiceInstance instance = loadBalancer.selectInstance(
|
||||
Collections.emptyList(),
|
||||
CustomLoadBalancer.LoadBalanceStrategy.ROUND_ROBIN);
|
||||
|
||||
assertNull(instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectInstance_NullList() {
|
||||
ServiceInstance instance = loadBalancer.selectInstance(
|
||||
null,
|
||||
CustomLoadBalancer.LoadBalanceStrategy.ROUND_ROBIN);
|
||||
|
||||
assertNull(instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetWeight() {
|
||||
ServiceInstance instance = instances.get(0);
|
||||
|
||||
loadBalancer.setWeight(instance, 5);
|
||||
|
||||
assertNotNull(instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementConnection() {
|
||||
ServiceInstance instance = instances.get(0);
|
||||
|
||||
loadBalancer.incrementConnection(instance);
|
||||
loadBalancer.incrementConnection(instance);
|
||||
|
||||
assertNotNull(instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecrementConnection() {
|
||||
ServiceInstance instance = instances.get(0);
|
||||
|
||||
loadBalancer.incrementConnection(instance);
|
||||
loadBalancer.incrementConnection(instance);
|
||||
loadBalancer.decrementConnection(instance);
|
||||
|
||||
assertNotNull(instance);
|
||||
}
|
||||
|
||||
private ServiceInstance createInstance(String host, int port) {
|
||||
return new DefaultServiceInstance(
|
||||
"service-" + host + "-" + port,
|
||||
"test-service",
|
||||
host,
|
||||
port,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package cn.novalon.gym.manage.gateway.metrics;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* GatewayMetrics单元测试
|
||||
*
|
||||
* 文件定义:测试网关指标收集器的核心功能
|
||||
* 涉及业务:请求统计、性能监控、活跃连接数统计
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
class GatewayMetricsTest {
|
||||
|
||||
private MeterRegistry meterRegistry;
|
||||
private GatewayMetrics gatewayMetrics;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
meterRegistry = new SimpleMeterRegistry();
|
||||
gatewayMetrics = new GatewayMetrics(meterRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementTotalRequests() {
|
||||
gatewayMetrics.incrementTotalRequests();
|
||||
|
||||
assertEquals(1, gatewayMetrics.getTotalRequests());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementSuccessRequests() {
|
||||
gatewayMetrics.incrementSuccessRequests();
|
||||
|
||||
assertEquals(1, gatewayMetrics.getSuccessRequests());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementFailedRequests() {
|
||||
gatewayMetrics.incrementFailedRequests();
|
||||
|
||||
assertEquals(1, gatewayMetrics.getFailedRequests());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementActiveConnections() {
|
||||
gatewayMetrics.incrementActiveConnections();
|
||||
|
||||
assertEquals(1, gatewayMetrics.getActiveConnections());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecrementActiveConnections() {
|
||||
gatewayMetrics.incrementActiveConnections();
|
||||
gatewayMetrics.incrementActiveConnections();
|
||||
gatewayMetrics.decrementActiveConnections();
|
||||
|
||||
assertEquals(1, gatewayMetrics.getActiveConnections());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRecordRequestDuration() {
|
||||
gatewayMetrics.recordRequestDuration("/api/users", Duration.ofMillis(100));
|
||||
|
||||
assertNotNull(meterRegistry.find("gateway.request.duration").timer());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleIncrements() {
|
||||
gatewayMetrics.incrementTotalRequests();
|
||||
gatewayMetrics.incrementTotalRequests();
|
||||
gatewayMetrics.incrementTotalRequests();
|
||||
|
||||
assertEquals(3, gatewayMetrics.getTotalRequests());
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package cn.novalon.gym.manage.gateway.monitor;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class PerformanceMonitorTest {
|
||||
|
||||
private PerformanceMonitor performanceMonitor;
|
||||
private MeterRegistry meterRegistry;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
meterRegistry = new SimpleMeterRegistry();
|
||||
performanceMonitor = new PerformanceMonitor(meterRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRecordRequest() {
|
||||
performanceMonitor.recordRequest("/api/test", 100);
|
||||
|
||||
assertEquals(1, performanceMonitor.getPathStats().size());
|
||||
assertTrue(performanceMonitor.getAverageProcessingTime() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSlowRequestDetection() {
|
||||
performanceMonitor.setSlowRequestThresholdMs(50);
|
||||
performanceMonitor.recordRequest("/api/test", 100);
|
||||
|
||||
assertEquals(1, performanceMonitor.getPathStats().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleRequests() {
|
||||
performanceMonitor.recordRequest("/api/test1", 100);
|
||||
performanceMonitor.recordRequest("/api/test2", 200);
|
||||
performanceMonitor.recordRequest("/api/test1", 150);
|
||||
|
||||
Map<String, PerformanceMonitor.PerformanceStats> stats = performanceMonitor.getPathStats();
|
||||
|
||||
assertEquals(2, stats.size());
|
||||
|
||||
PerformanceMonitor.PerformanceStats test1Stats = stats.get("/api/test1");
|
||||
assertNotNull(test1Stats);
|
||||
assertEquals(2, test1Stats.getRequestCount());
|
||||
assertEquals(125.0, test1Stats.getAverageTime());
|
||||
assertEquals(150, test1Stats.getMaxTime());
|
||||
assertEquals(100, test1Stats.getMinTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMemoryStats() {
|
||||
Map<String, Object> memoryStats = performanceMonitor.getMemoryStats();
|
||||
|
||||
assertNotNull(memoryStats);
|
||||
assertTrue(memoryStats.containsKey("totalMemory"));
|
||||
assertTrue(memoryStats.containsKey("freeMemory"));
|
||||
assertTrue(memoryStats.containsKey("usedMemory"));
|
||||
assertTrue(memoryStats.containsKey("maxMemory"));
|
||||
assertTrue(memoryStats.containsKey("memoryUsage"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThreadStats() {
|
||||
Map<String, Object> threadStats = performanceMonitor.getThreadStats();
|
||||
|
||||
assertNotNull(threadStats);
|
||||
assertTrue(threadStats.containsKey("threadCount"));
|
||||
assertTrue(threadStats.containsKey("peakThreadCount"));
|
||||
assertTrue(threadStats.containsKey("daemonThreadCount"));
|
||||
assertTrue(threadStats.containsKey("totalStartedThreadCount"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMemoryUsage() {
|
||||
double memoryUsage = performanceMonitor.getMemoryUsage();
|
||||
|
||||
assertTrue(memoryUsage >= 0.0);
|
||||
assertTrue(memoryUsage <= 1.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAverageProcessingTime_NoRequests() {
|
||||
assertEquals(0.0, performanceMonitor.getAverageProcessingTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAverageProcessingTime_WithRequests() {
|
||||
performanceMonitor.recordRequest("/api/test1", 100);
|
||||
performanceMonitor.recordRequest("/api/test2", 200);
|
||||
|
||||
assertEquals(150.0, performanceMonitor.getAverageProcessingTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearStats() {
|
||||
performanceMonitor.recordRequest("/api/test", 100);
|
||||
performanceMonitor.clearStats();
|
||||
|
||||
assertEquals(0, performanceMonitor.getPathStats().size());
|
||||
assertEquals(0.0, performanceMonitor.getAverageProcessingTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetSlowRequestThreshold() {
|
||||
performanceMonitor.setSlowRequestThresholdMs(500);
|
||||
performanceMonitor.recordRequest("/api/test", 600);
|
||||
|
||||
assertEquals(1, performanceMonitor.getPathStats().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetMemoryWarningThreshold() {
|
||||
performanceMonitor.setMemoryWarningThreshold(0.9);
|
||||
performanceMonitor.recordRequest("/api/test", 100);
|
||||
|
||||
assertEquals(1, performanceMonitor.getPathStats().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPerformanceStats() {
|
||||
PerformanceMonitor.PerformanceStats stats = new PerformanceMonitor.PerformanceStats();
|
||||
|
||||
stats.recordRequest(100);
|
||||
stats.recordRequest(200);
|
||||
stats.recordRequest(150);
|
||||
|
||||
assertEquals(3, stats.getRequestCount());
|
||||
assertEquals(150.0, stats.getAverageTime());
|
||||
assertEquals(200, stats.getMaxTime());
|
||||
assertEquals(100, stats.getMinTime());
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package cn.novalon.gym.manage.gateway.route;
|
||||
|
||||
import cn.novalon.gym.manage.gateway.service.impl.DynamicRouteService;
|
||||
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.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* DynamicRouteService单元测试
|
||||
*
|
||||
* 文件定义:测试动态路由服务的核心功能
|
||||
* 涉及业务:路由增删改查、路由刷新
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DynamicRouteServiceTest {
|
||||
|
||||
@Mock
|
||||
private RouteDefinitionWriter routeDefinitionWriter;
|
||||
|
||||
@Mock
|
||||
private RouteDefinitionLocator routeDefinitionLocator;
|
||||
|
||||
@Mock
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
private DynamicRouteService dynamicRouteService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(routeDefinitionLocator.getRouteDefinitions())
|
||||
.thenReturn(Flux.empty());
|
||||
|
||||
dynamicRouteService = new DynamicRouteService(
|
||||
routeDefinitionWriter,
|
||||
routeDefinitionLocator,
|
||||
publisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddRoute_Success() {
|
||||
RouteDefinition routeDefinition = createRouteDefinition("test-route");
|
||||
|
||||
when(routeDefinitionWriter.save(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(dynamicRouteService.addRoute(routeDefinition))
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
|
||||
verify(routeDefinitionWriter).save(any());
|
||||
verify(publisher).publishEvent(any(RefreshRoutesEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddRoute_NullRoute() {
|
||||
StepVerifier.create(dynamicRouteService.addRoute(null))
|
||||
.expectNext(false)
|
||||
.verifyComplete();
|
||||
|
||||
verify(routeDefinitionWriter, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteRoute_Success() {
|
||||
String routeId = "test-route";
|
||||
RouteDefinition routeDefinition = createRouteDefinition(routeId);
|
||||
|
||||
// 先添加路由到缓存中
|
||||
when(routeDefinitionWriter.save(any())).thenReturn(Mono.empty());
|
||||
StepVerifier.create(dynamicRouteService.addRoute(routeDefinition))
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
|
||||
// 然后删除路由
|
||||
when(routeDefinitionWriter.delete(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(dynamicRouteService.deleteRoute(routeId))
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
|
||||
verify(routeDefinitionWriter).delete(any());
|
||||
verify(publisher, times(2)).publishEvent(any(RefreshRoutesEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteRoute_NullId() {
|
||||
StepVerifier.create(dynamicRouteService.deleteRoute(null))
|
||||
.expectNext(false)
|
||||
.verifyComplete();
|
||||
|
||||
verify(routeDefinitionWriter, never()).delete(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllRoutes() {
|
||||
RouteDefinition route1 = createRouteDefinition("route1");
|
||||
RouteDefinition route2 = createRouteDefinition("route2");
|
||||
|
||||
when(routeDefinitionWriter.save(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(dynamicRouteService.addRoute(route1))
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(dynamicRouteService.addRoute(route2))
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(dynamicRouteService.getRoutes().collectList())
|
||||
.assertNext(routes -> {
|
||||
assertNotNull(routes);
|
||||
assertTrue(routes.size() >= 2);
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRouteCount() {
|
||||
RouteDefinition route = createRouteDefinition("test-route");
|
||||
|
||||
when(routeDefinitionWriter.save(any())).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(dynamicRouteService.addRoute(route))
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(dynamicRouteService.getRouteCount())
|
||||
.assertNext(count -> assertTrue(count >= 1))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
private RouteDefinition createRouteDefinition(String id) {
|
||||
RouteDefinition routeDefinition = new RouteDefinition();
|
||||
routeDefinition.setId(id);
|
||||
routeDefinition.setUri(java.net.URI.create("http://localhost:8080"));
|
||||
return routeDefinition;
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
package cn.novalon.gym.manage.gateway.service.impl;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class JwtKeyServiceImplTest {
|
||||
|
||||
@InjectMocks
|
||||
private JwtKeyServiceImpl jwtKeyService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(jwtKeyService, "configuredSecret", null);
|
||||
ReflectionTestUtils.setField(jwtKeyService, "encryptionPassword", "testEncryptionPassword");
|
||||
ReflectionTestUtils.setField(jwtKeyService, "rotationEnabled", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitializeKeys_GeneratesNewKey() {
|
||||
jwtKeyService.initializeKeys();
|
||||
|
||||
String version = jwtKeyService.getCurrentKeyVersion();
|
||||
SecretKey key = jwtKeyService.getCurrentSigningKey();
|
||||
|
||||
assertNotNull(version);
|
||||
assertNotNull(key);
|
||||
assertEquals("v1", version);
|
||||
assertEquals("AES", key.getAlgorithm());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateSecureKey_GeneratesValidKey() {
|
||||
String key = jwtKeyService.generateSecureKey();
|
||||
|
||||
assertNotNull(key);
|
||||
assertFalse(key.isEmpty());
|
||||
assertTrue(jwtKeyService.validateKeyStrength(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateKeyStrength_ValidKey() {
|
||||
String validKey = "StrongPassword123ABC!@#XYZabcdefg";
|
||||
assertTrue(jwtKeyService.validateKeyStrength(validKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateKeyStrength_WeakKey() {
|
||||
String weakKey = "weak";
|
||||
assertFalse(jwtKeyService.validateKeyStrength(weakKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateKeyStrength_NullKey() {
|
||||
assertFalse(jwtKeyService.validateKeyStrength(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateKeyStrength_ShortKey() {
|
||||
String shortKey = "Short1!";
|
||||
assertFalse(jwtKeyService.validateKeyStrength(shortKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptKey_WithPassword() {
|
||||
String originalKey = "MySecretKey123!";
|
||||
String encryptedKey = jwtKeyService.encryptKey(originalKey);
|
||||
|
||||
assertNotNull(encryptedKey);
|
||||
assertNotEquals(originalKey, encryptedKey);
|
||||
assertTrue(encryptedKey.length() > originalKey.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptDecryptKey_RoundTrip() {
|
||||
String originalKey = "MySecretKey123!";
|
||||
String encryptedKey = jwtKeyService.encryptKey(originalKey);
|
||||
String decryptedKey = jwtKeyService.decryptKey(encryptedKey);
|
||||
|
||||
assertNotNull(encryptedKey);
|
||||
assertNotNull(decryptedKey);
|
||||
assertEquals(originalKey, decryptedKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRotateKey_CreatesNewVersion() {
|
||||
jwtKeyService.initializeKeys();
|
||||
String oldVersion = jwtKeyService.getCurrentKeyVersion();
|
||||
|
||||
jwtKeyService.rotateKey();
|
||||
|
||||
String newVersion = jwtKeyService.getCurrentKeyVersion();
|
||||
SecretKey newKey = jwtKeyService.getCurrentSigningKey();
|
||||
|
||||
assertNotEquals(oldVersion, newVersion);
|
||||
assertEquals("v2", newVersion);
|
||||
assertNotNull(newKey);
|
||||
assertEquals("AES", newKey.getAlgorithm());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetSigningKeyByVersion_ReturnsCorrectKey() {
|
||||
jwtKeyService.initializeKeys();
|
||||
SecretKey v1Key = jwtKeyService.getSigningKeyByVersion("v1");
|
||||
|
||||
assertNotNull(v1Key);
|
||||
assertEquals("AES", v1Key.getAlgorithm());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetSigningKeyByVersion_InvalidVersion() {
|
||||
jwtKeyService.initializeKeys();
|
||||
SecretKey invalidKey = jwtKeyService.getSigningKeyByVersion("v999");
|
||||
|
||||
assertNull(invalidKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRotateKey_Disabled() {
|
||||
ReflectionTestUtils.setField(jwtKeyService, "rotationEnabled", false);
|
||||
jwtKeyService.initializeKeys();
|
||||
String oldVersion = jwtKeyService.getCurrentKeyVersion();
|
||||
|
||||
jwtKeyService.rotateKey();
|
||||
|
||||
String newVersion = jwtKeyService.getCurrentKeyVersion();
|
||||
assertEquals(oldVersion, newVersion);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldRotateKey_NewKey() {
|
||||
jwtKeyService.initializeKeys();
|
||||
|
||||
String currentVersion = jwtKeyService.getCurrentKeyVersion();
|
||||
SecretKey currentKey = jwtKeyService.getCurrentSigningKey();
|
||||
|
||||
assertNotNull(currentVersion, "Current version should not be null");
|
||||
assertNotNull(currentKey, "Current signing key should not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleRotations_CreatesMultipleVersions() {
|
||||
jwtKeyService.initializeKeys();
|
||||
|
||||
jwtKeyService.rotateKey();
|
||||
assertEquals("v2", jwtKeyService.getCurrentKeyVersion());
|
||||
|
||||
jwtKeyService.rotateKey();
|
||||
assertEquals("v3", jwtKeyService.getCurrentKeyVersion());
|
||||
|
||||
jwtKeyService.rotateKey();
|
||||
assertEquals("v4", jwtKeyService.getCurrentKeyVersion());
|
||||
|
||||
assertNotNull(jwtKeyService.getSigningKeyByVersion("v1"));
|
||||
assertNotNull(jwtKeyService.getSigningKeyByVersion("v2"));
|
||||
assertNotNull(jwtKeyService.getSigningKeyByVersion("v3"));
|
||||
assertNotNull(jwtKeyService.getSigningKeyByVersion("v4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptKey_WithoutPassword() {
|
||||
ReflectionTestUtils.setField(jwtKeyService, "encryptionPassword", "");
|
||||
String originalKey = "MySecretKey123!";
|
||||
String encryptedKey = jwtKeyService.encryptKey(originalKey);
|
||||
|
||||
assertEquals(originalKey, encryptedKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptKey_WithoutPassword() {
|
||||
ReflectionTestUtils.setField(jwtKeyService, "encryptionPassword", "");
|
||||
String originalKey = "MySecretKey123!";
|
||||
String decryptedKey = jwtKeyService.decryptKey(originalKey);
|
||||
|
||||
assertEquals(originalKey, decryptedKey);
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package cn.novalon.gym.manage.gateway.service.impl;
|
||||
|
||||
import cn.novalon.gym.manage.gateway.model.Permission;
|
||||
import cn.novalon.gym.manage.gateway.model.Role;
|
||||
import cn.novalon.gym.manage.gateway.model.User;
|
||||
import cn.novalon.gym.manage.gateway.service.PermissionService;
|
||||
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.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class PermissionServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private WebClient.Builder webClientBuilder;
|
||||
|
||||
@Mock
|
||||
private WebClient webClient;
|
||||
|
||||
@Mock
|
||||
private WebClient.RequestHeadersUriSpec<?> requestHeadersUriSpec;
|
||||
|
||||
@Mock
|
||||
private WebClient.RequestHeadersSpec<?> requestHeadersSpec;
|
||||
|
||||
@Mock
|
||||
private WebClient.ResponseSpec responseSpec;
|
||||
|
||||
private PermissionService permissionService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
doReturn(webClient).when(webClientBuilder).build();
|
||||
doReturn(requestHeadersUriSpec).when(webClient).get();
|
||||
doReturn(requestHeadersSpec).when(requestHeadersUriSpec).uri(anyString());
|
||||
doReturn(responseSpec).when(requestHeadersSpec).retrieve();
|
||||
permissionService = new PermissionServiceImpl(webClientBuilder, "http://localhost:8084");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserById_Success() {
|
||||
User expectedUser = new User(1L, "testuser", "test@example.com", "1234567890", 1, System.currentTimeMillis(), System.currentTimeMillis());
|
||||
|
||||
doReturn(Mono.just(expectedUser)).when(responseSpec).bodyToMono(eq(User.class));
|
||||
|
||||
User user = permissionService.getUserById(1L);
|
||||
|
||||
assertNotNull(user);
|
||||
assertEquals("testuser", user.getUsername());
|
||||
verify(webClient).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserById_NullUserId() {
|
||||
User user = permissionService.getUserById(null);
|
||||
|
||||
assertNull(user);
|
||||
verify(webClient, never()).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserRoles_Success() {
|
||||
List<Role> expectedRoles = Arrays.asList(
|
||||
new Role(1L, "ADMIN", "Administrator", "Admin role", 1, System.currentTimeMillis(), System.currentTimeMillis()),
|
||||
new Role(2L, "USER", "User", "User role", 1, System.currentTimeMillis(), System.currentTimeMillis())
|
||||
);
|
||||
|
||||
doReturn(Mono.just(expectedRoles.toArray(new Role[0]))).when(responseSpec).bodyToMono(eq(Role[].class));
|
||||
|
||||
List<Role> roles = permissionService.getUserRoles(1L);
|
||||
|
||||
assertNotNull(roles);
|
||||
assertEquals(2, roles.size());
|
||||
verify(webClient).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserRoles_NullUserId() {
|
||||
List<Role> roles = permissionService.getUserRoles(null);
|
||||
|
||||
assertNotNull(roles);
|
||||
assertTrue(roles.isEmpty());
|
||||
verify(webClient, never()).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserPermissions_Success() {
|
||||
Set<Permission> expectedPermissions = new HashSet<>(Arrays.asList(
|
||||
new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis()),
|
||||
new Permission(2L, "user:write", "Write User", "API", "/api/users/**", "POST", "Write user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
|
||||
));
|
||||
|
||||
doReturn(Mono.just(expectedPermissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));
|
||||
|
||||
Set<Permission> permissions = permissionService.getUserPermissions(1L);
|
||||
|
||||
assertNotNull(permissions);
|
||||
assertEquals(2, permissions.size());
|
||||
verify(webClient).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserPermissions_NullUserId() {
|
||||
Set<Permission> permissions = permissionService.getUserPermissions(null);
|
||||
|
||||
assertNotNull(permissions);
|
||||
assertTrue(permissions.isEmpty());
|
||||
verify(webClient, never()).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHasPermission_True() {
|
||||
Set<Permission> permissions = new HashSet<>(Arrays.asList(
|
||||
new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
|
||||
));
|
||||
|
||||
doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));
|
||||
|
||||
boolean hasPermission = permissionService.hasPermission(1L, "/api/users/123", "GET");
|
||||
|
||||
assertTrue(hasPermission);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHasPermission_False() {
|
||||
Set<Permission> permissions = new HashSet<>(Arrays.asList(
|
||||
new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
|
||||
));
|
||||
|
||||
doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));
|
||||
|
||||
boolean hasPermission = permissionService.hasPermission(1L, "/api/users/123", "POST");
|
||||
|
||||
assertFalse(hasPermission);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHasPermission_NullUserId() {
|
||||
boolean hasPermission = permissionService.hasPermission(null, "/api/users/123", "GET");
|
||||
|
||||
assertFalse(hasPermission);
|
||||
verify(webClient, never()).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPermissionPaths_Success() {
|
||||
Set<Permission> permissions = new HashSet<>(Arrays.asList(
|
||||
new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis()),
|
||||
new Permission(2L, "user:write", "Write User", "API", "/api/users/**", "POST", "Write user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
|
||||
));
|
||||
|
||||
doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));
|
||||
|
||||
Set<String> paths = permissionService.getPermissionPaths(1L, "GET");
|
||||
|
||||
assertNotNull(paths);
|
||||
assertEquals(1, paths.size());
|
||||
assertTrue(paths.contains("/api/users/**"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearCache_Success() {
|
||||
User user = new User(1L, "testuser", "test@example.com", "1234567890", 1, System.currentTimeMillis(), System.currentTimeMillis());
|
||||
List<Role> roles = Arrays.asList(new Role(1L, "ADMIN", "Administrator", "Admin role", 1, System.currentTimeMillis(), System.currentTimeMillis()));
|
||||
Set<Permission> permissions = new HashSet<>(Arrays.asList(
|
||||
new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
|
||||
));
|
||||
|
||||
doReturn(Mono.just(user)).when(responseSpec).bodyToMono(eq(User.class));
|
||||
doReturn(Mono.just(roles.toArray(new Role[0]))).when(responseSpec).bodyToMono(eq(Role[].class));
|
||||
doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));
|
||||
|
||||
permissionService.getUserById(1L);
|
||||
permissionService.getUserRoles(1L);
|
||||
permissionService.getUserPermissions(1L);
|
||||
|
||||
permissionService.clearCache(1L);
|
||||
|
||||
verify(webClient, times(3)).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearAllCache_Success() {
|
||||
User user = new User(1L, "testuser", "test@example.com", "1234567890", 1, System.currentTimeMillis(), System.currentTimeMillis());
|
||||
List<Role> roles = Arrays.asList(new Role(1L, "ADMIN", "Administrator", "Admin role", 1, System.currentTimeMillis(), System.currentTimeMillis()));
|
||||
Set<Permission> permissions = new HashSet<>(Arrays.asList(
|
||||
new Permission(1L, "user:read", "Read User", "API", "/api/users/**", "GET", "Read user permissions", 1, System.currentTimeMillis(), System.currentTimeMillis())
|
||||
));
|
||||
|
||||
doReturn(Mono.just(user)).when(responseSpec).bodyToMono(eq(User.class));
|
||||
doReturn(Mono.just(roles.toArray(new Role[0]))).when(responseSpec).bodyToMono(eq(Role[].class));
|
||||
doReturn(Mono.just(permissions.toArray(new Permission[0]))).when(responseSpec).bodyToMono(eq(Permission[].class));
|
||||
|
||||
permissionService.getUserById(1L);
|
||||
permissionService.getUserRoles(1L);
|
||||
permissionService.getUserPermissions(1L);
|
||||
|
||||
permissionService.clearAllCache();
|
||||
|
||||
permissionService.getUserById(1L);
|
||||
permissionService.getUserRoles(1L);
|
||||
permissionService.getUserPermissions(1L);
|
||||
|
||||
verify(webClient, times(6)).get();
|
||||
}
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
package cn.novalon.gym.manage.gateway.service.impl;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* SignatureServiceImpl单元测试
|
||||
*
|
||||
* 文件定义:测试签名服务的核心功能
|
||||
* 涉及业务:签名生成、签名验证、时间戳验证、nonce防重放
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SignatureServiceImplTest {
|
||||
|
||||
@InjectMocks
|
||||
private SignatureServiceImpl signatureService;
|
||||
|
||||
private static final String TEST_SECRET = "TestSecretKey123";
|
||||
private static final String TEST_METHOD = "GET";
|
||||
private static final String TEST_PATH = "/api/users";
|
||||
private static final String TEST_QUERY = "page=1&size=10";
|
||||
private static final String TEST_BODY = "";
|
||||
private static final String TEST_NONCE = "test-nonce-12345";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(signatureService, "signatureEnabled", true);
|
||||
ReflectionTestUtils.setField(signatureService, "maxAgeMinutes", 5);
|
||||
ReflectionTestUtils.setField(signatureService, "nonceCacheSize", 10000);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateSignature_ShouldGenerateValidSignature() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
String signature = signatureService.generateSignature(
|
||||
TEST_METHOD, TEST_PATH, TEST_QUERY, TEST_BODY, timestamp, TEST_NONCE, TEST_SECRET);
|
||||
|
||||
assertNotNull(signature);
|
||||
assertFalse(signature.isEmpty());
|
||||
assertTrue(signature.length() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateSignature_ShouldGenerateSameSignatureForSameInput() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
String signature1 = signatureService.generateSignature(
|
||||
TEST_METHOD, TEST_PATH, TEST_QUERY, TEST_BODY, timestamp, TEST_NONCE, TEST_SECRET);
|
||||
String signature2 = signatureService.generateSignature(
|
||||
TEST_METHOD, TEST_PATH, TEST_QUERY, TEST_BODY, timestamp, TEST_NONCE, TEST_SECRET);
|
||||
|
||||
assertEquals(signature1, signature2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateSignature_ShouldGenerateDifferentSignatureForDifferentInput() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
String signature1 = signatureService.generateSignature(
|
||||
TEST_METHOD, TEST_PATH, TEST_QUERY, TEST_BODY, timestamp, TEST_NONCE, TEST_SECRET);
|
||||
String signature2 = signatureService.generateSignature(
|
||||
"POST", TEST_PATH, TEST_QUERY, TEST_BODY, timestamp, TEST_NONCE, TEST_SECRET);
|
||||
|
||||
assertNotEquals(signature1, signature2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifySignature_WithValidSignature_ShouldReturnTrue() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String signature = signatureService.generateSignature(
|
||||
TEST_METHOD, TEST_PATH, TEST_QUERY, TEST_BODY, timestamp, TEST_NONCE, TEST_SECRET);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, TEST_PATH + "?" + TEST_QUERY)
|
||||
.header("X-Signature", signature)
|
||||
.header("X-Timestamp", String.valueOf(timestamp))
|
||||
.header("X-Nonce", TEST_NONCE)
|
||||
.build();
|
||||
|
||||
boolean isValid = signatureService.verifySignature(request, TEST_SECRET);
|
||||
|
||||
assertTrue(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifySignature_WithInvalidSignature_ShouldReturnFalse() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, TEST_PATH)
|
||||
.header("X-Signature", "invalid-signature")
|
||||
.header("X-Timestamp", String.valueOf(timestamp))
|
||||
.header("X-Nonce", TEST_NONCE)
|
||||
.build();
|
||||
|
||||
boolean isValid = signatureService.verifySignature(request, TEST_SECRET);
|
||||
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifySignature_WithMissingHeaders_ShouldReturnFalse() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, TEST_PATH)
|
||||
.build();
|
||||
|
||||
boolean isValid = signatureService.verifySignature(request, TEST_SECRET);
|
||||
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifySignature_WithExpiredTimestamp_ShouldReturnFalse() {
|
||||
long expiredTimestamp = System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10);
|
||||
String signature = signatureService.generateSignature(
|
||||
TEST_METHOD, TEST_PATH, TEST_QUERY, TEST_BODY, expiredTimestamp, TEST_NONCE, TEST_SECRET);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, TEST_PATH)
|
||||
.header("X-Signature", signature)
|
||||
.header("X-Timestamp", String.valueOf(expiredTimestamp))
|
||||
.header("X-Nonce", TEST_NONCE)
|
||||
.build();
|
||||
|
||||
boolean isValid = signatureService.verifySignature(request, TEST_SECRET);
|
||||
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifySignature_WithUsedNonce_ShouldReturnFalse() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String signature = signatureService.generateSignature(
|
||||
TEST_METHOD, TEST_PATH, TEST_QUERY, TEST_BODY, timestamp, TEST_NONCE, TEST_SECRET);
|
||||
|
||||
signatureService.recordNonce(TEST_NONCE);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, TEST_PATH)
|
||||
.header("X-Signature", signature)
|
||||
.header("X-Timestamp", String.valueOf(timestamp))
|
||||
.header("X-Nonce", TEST_NONCE)
|
||||
.build();
|
||||
|
||||
boolean isValid = signatureService.verifySignature(request, TEST_SECRET);
|
||||
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsTimestampValid_WithValidTimestamp_ShouldReturnTrue() {
|
||||
long validTimestamp = System.currentTimeMillis();
|
||||
|
||||
boolean isValid = signatureService.isTimestampValid(validTimestamp, 5);
|
||||
|
||||
assertTrue(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsTimestampValid_WithExpiredTimestamp_ShouldReturnFalse() {
|
||||
long expiredTimestamp = System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10);
|
||||
|
||||
boolean isValid = signatureService.isTimestampValid(expiredTimestamp, 5);
|
||||
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsTimestampValid_WithFutureTimestamp_ShouldReturnFalse() {
|
||||
long futureTimestamp = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10);
|
||||
|
||||
boolean isValid = signatureService.isTimestampValid(futureTimestamp, 5);
|
||||
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsNonceUsed_WithNewNonce_ShouldReturnFalse() {
|
||||
boolean isUsed = signatureService.isNonceUsed("new-nonce-123");
|
||||
|
||||
assertFalse(isUsed);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsNonceUsed_WithUsedNonce_ShouldReturnTrue() {
|
||||
String nonce = "used-nonce-123";
|
||||
signatureService.recordNonce(nonce);
|
||||
|
||||
boolean isUsed = signatureService.isNonceUsed(nonce);
|
||||
|
||||
assertTrue(isUsed);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRecordNonce_ShouldIncreaseCacheSize() {
|
||||
int initialSize = signatureService.getNonceCacheSize();
|
||||
|
||||
signatureService.recordNonce("test-nonce-1");
|
||||
signatureService.recordNonce("test-nonce-2");
|
||||
signatureService.recordNonce("test-nonce-3");
|
||||
|
||||
int finalSize = signatureService.getNonceCacheSize();
|
||||
assertEquals(initialSize + 3, finalSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCleanupExpiredNonces_ShouldRemoveExpiredEntries() {
|
||||
ReflectionTestUtils.setField(signatureService, "nonceCacheSize", 5);
|
||||
|
||||
signatureService.recordNonce("nonce-1");
|
||||
signatureService.recordNonce("nonce-2");
|
||||
signatureService.recordNonce("nonce-3");
|
||||
signatureService.recordNonce("nonce-4");
|
||||
signatureService.recordNonce("nonce-5");
|
||||
signatureService.recordNonce("nonce-6");
|
||||
|
||||
int cacheSize = signatureService.getNonceCacheSize();
|
||||
assertTrue(cacheSize <= 6);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifySignature_WhenDisabled_ShouldReturnTrue() {
|
||||
ReflectionTestUtils.setField(signatureService, "signatureEnabled", false);
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, TEST_PATH)
|
||||
.build();
|
||||
|
||||
boolean isValid = signatureService.verifySignature(request, TEST_SECRET);
|
||||
|
||||
assertTrue(isValid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
spring:
|
||||
application:
|
||||
name: manage-gateway
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: user-service
|
||||
uri: http://localhost:8084
|
||||
predicates:
|
||||
- Path=/api/users/**
|
||||
- id: auth-service
|
||||
uri: http://localhost:8083
|
||||
predicates:
|
||||
- Path=/api/auth/**
|
||||
|
||||
user:
|
||||
service:
|
||||
url: http://localhost:8084
|
||||
|
||||
permission:
|
||||
cache:
|
||||
expiry:
|
||||
minutes: 5
|
||||
|
||||
logging:
|
||||
level:
|
||||
cn.novalon.manage.gateway: DEBUG
|
||||
org.springframework.cloud.gateway: DEBUG
|
||||
org.springframework.web.reactive: DEBUG
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
Reference in New Issue
Block a user