parent
65ae5ec2bc
commit
efb6f4a057
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.polaris.circuitbreaker.common;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for ${@link PolarisResultToErrorCode}.
|
||||||
|
*
|
||||||
|
* @author Shedfree Wu
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PolarisResultToErrorCodeTest {
|
||||||
|
|
||||||
|
private final PolarisResultToErrorCode converter = new PolarisResultToErrorCode();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnSuccess() {
|
||||||
|
assertThat(converter.onSuccess("any value")).isEqualTo(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnErrorWithWebClientResponseException() {
|
||||||
|
// Given
|
||||||
|
WebClientResponseException exception = WebClientResponseException.create(
|
||||||
|
404, "Not Found", null, null, null);
|
||||||
|
|
||||||
|
// When
|
||||||
|
int errorCode = converter.onError(exception);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(errorCode).isEqualTo(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnErrorWithCircuitBreakerStatusCodeException() {
|
||||||
|
// When
|
||||||
|
int errorCode = converter.onError(new RuntimeException("test"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(errorCode).isEqualTo(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnErrorWithUnknownException() {
|
||||||
|
// Given
|
||||||
|
RuntimeException exception = new RuntimeException("Unknown error");
|
||||||
|
|
||||||
|
// When
|
||||||
|
int errorCode = converter.onError(exception);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(errorCode).isEqualTo(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCheckClassExist() throws Exception {
|
||||||
|
// Given
|
||||||
|
Method checkClassExist = PolarisResultToErrorCode.class.getDeclaredMethod("checkClassExist", String.class);
|
||||||
|
checkClassExist.setAccessible(true);
|
||||||
|
|
||||||
|
PolarisResultToErrorCode converter = new PolarisResultToErrorCode();
|
||||||
|
|
||||||
|
// test exist class
|
||||||
|
boolean result1 = (boolean) checkClassExist.invoke(converter, "java.lang.String");
|
||||||
|
assertThat(result1).isTrue();
|
||||||
|
|
||||||
|
// test not exist class
|
||||||
|
boolean result2 = (boolean) checkClassExist.invoke(converter, "com.nonexistent.Class");
|
||||||
|
assertThat(result2).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.polaris.circuitbreaker.instrument.feign;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.tencent.cloud.polaris.circuitbreaker.exception.FallbackWrapperException;
|
||||||
|
import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
|
||||||
|
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
|
||||||
|
import feign.InvocationHandlerFactory;
|
||||||
|
import feign.Target;
|
||||||
|
import feign.codec.Decoder;
|
||||||
|
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.openfeign.FallbackFactory;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for ${@link PolarisFeignCircuitBreakerInvocationHandler}.
|
||||||
|
*
|
||||||
|
* @author Shedfree Wu
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PolarisFeignCircuitBreakerInvocationHandlerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Target<?> target;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private InvocationHandlerFactory.MethodHandler methodHandler;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private FallbackFactory<TestInterface> fallbackFactory;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Decoder decoder;
|
||||||
|
|
||||||
|
private Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
|
||||||
|
private PolarisFeignCircuitBreakerInvocationHandler handler;
|
||||||
|
|
||||||
|
private Method testMethod;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws Exception {
|
||||||
|
dispatch = new HashMap<>();
|
||||||
|
|
||||||
|
testMethod = TestInterface.class.getDeclaredMethod("testMethod");
|
||||||
|
dispatch.put(testMethod, methodHandler);
|
||||||
|
|
||||||
|
handler = new PolarisFeignCircuitBreakerInvocationHandler(
|
||||||
|
target,
|
||||||
|
dispatch,
|
||||||
|
fallbackFactory,
|
||||||
|
decoder
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testConstructorWithNullTarget() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
new PolarisFeignCircuitBreakerInvocationHandler(
|
||||||
|
null, dispatch, fallbackFactory, decoder
|
||||||
|
)
|
||||||
|
).isInstanceOf(NullPointerException.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testConstructorWithNullDispatch() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
new PolarisFeignCircuitBreakerInvocationHandler(
|
||||||
|
target, null, fallbackFactory, decoder
|
||||||
|
)).isInstanceOf(NullPointerException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testToFallbackMethod() throws Exception {
|
||||||
|
Method method = TestInterface.class.getMethod("testMethod");
|
||||||
|
Map<Method, InvocationHandlerFactory.MethodHandler> testDispatch = new HashMap<>();
|
||||||
|
testDispatch.put(method, methodHandler);
|
||||||
|
|
||||||
|
Map<Method, Method> result = PolarisFeignCircuitBreakerInvocationHandler.toFallbackMethod(testDispatch);
|
||||||
|
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.containsKey(method)).isTrue();
|
||||||
|
assertThat(result.get(method)).isEqualTo(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEqualsMethod() throws Throwable {
|
||||||
|
Method equalsMethod = Object.class.getMethod("equals", Object.class);
|
||||||
|
Object mockProxy = mock(Object.class);
|
||||||
|
|
||||||
|
// Test equals with null
|
||||||
|
assertThat((Boolean) handler.invoke(mockProxy, equalsMethod, new Object[] {null})).isFalse();
|
||||||
|
|
||||||
|
// Test equals with non-proxy object
|
||||||
|
assertThat((Boolean) handler.invoke(mockProxy, equalsMethod, new Object[] {new Object()})).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testToStringMethod() throws Throwable {
|
||||||
|
Method toStringMethod = Object.class.getMethod("toString");
|
||||||
|
Object mockProxy = mock(Object.class);
|
||||||
|
when(target.toString()).thenReturn("TestTarget");
|
||||||
|
|
||||||
|
assertThat(handler.invoke(mockProxy, toStringMethod, null)).isEqualTo("TestTarget");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCustomFallbackFactoryWithFallbackError() throws Throwable {
|
||||||
|
// Arrange
|
||||||
|
handler = new PolarisFeignCircuitBreakerInvocationHandler(target, dispatch, fallbackFactory, decoder);
|
||||||
|
Exception originalException = new RuntimeException("Original error");
|
||||||
|
|
||||||
|
when(methodHandler.invoke(any())).thenThrow(originalException);
|
||||||
|
TestImpl testImpl = new TestImpl();
|
||||||
|
when(fallbackFactory.create(any())).thenReturn(testImpl);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
assertThatThrownBy(() -> handler.invoke(null, testMethod, new Object[] {})).isInstanceOf(FallbackWrapperException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCustomFallbackFactoryWithFallbackError2() throws Throwable {
|
||||||
|
// Arrange
|
||||||
|
handler = new PolarisFeignCircuitBreakerInvocationHandler(target, dispatch, fallbackFactory, decoder);
|
||||||
|
Exception originalException = new RuntimeException("Original error");
|
||||||
|
|
||||||
|
when(methodHandler.invoke(any())).thenThrow(originalException);
|
||||||
|
TestImpl2 testImpl = new TestImpl2();
|
||||||
|
when(fallbackFactory.create(any())).thenReturn(testImpl);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
assertThatThrownBy(() -> handler.invoke(null, testMethod, new Object[] {})).
|
||||||
|
isInstanceOf(RuntimeException.class).hasMessage("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDefaultFallbackCreation() throws Throwable {
|
||||||
|
// Arrange
|
||||||
|
handler = new PolarisFeignCircuitBreakerInvocationHandler(target, dispatch, null, decoder);
|
||||||
|
CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, new HashMap<>(), "mock body");
|
||||||
|
CallAbortedException originalException = new CallAbortedException("test rule", fallbackInfo);
|
||||||
|
|
||||||
|
Object expected = new Object();
|
||||||
|
when(methodHandler.invoke(any())).thenThrow(originalException);
|
||||||
|
when(decoder.decode(any(), any())).thenReturn(expected);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Object result = handler.invoke(null, testMethod, new Object[] {});
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertThat(result).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEquals() {
|
||||||
|
PolarisFeignCircuitBreakerInvocationHandler testHandler = new PolarisFeignCircuitBreakerInvocationHandler(
|
||||||
|
target,
|
||||||
|
dispatch,
|
||||||
|
fallbackFactory,
|
||||||
|
decoder
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(testHandler).isEqualTo(handler);
|
||||||
|
assertThat(testHandler.hashCode()).isEqualTo(handler.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestInterface {
|
||||||
|
String testMethod() throws InvocationTargetException;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestImpl implements TestInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String testMethod() throws InvocationTargetException {
|
||||||
|
throw new InvocationTargetException(new RuntimeException("test"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestImpl2 implements TestInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String testMethod() throws InvocationTargetException {
|
||||||
|
throw new RuntimeException("test");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.polaris.circuitbreaker.instrument.feign;
|
||||||
|
|
||||||
|
import feign.Feign;
|
||||||
|
import feign.RequestLine;
|
||||||
|
import feign.Target;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link PolarisFeignCircuitBreaker}.
|
||||||
|
*/
|
||||||
|
public class PolarisFeignCircuitBreakerTest {
|
||||||
|
|
||||||
|
private PolarisFeignCircuitBreaker.Builder builder;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
builder = PolarisFeignCircuitBreaker.builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilderNotNull() {
|
||||||
|
assertThat(builder).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTargetWithFallback() {
|
||||||
|
// Mock the target
|
||||||
|
Class<MyService> targetType = MyService.class;
|
||||||
|
String name = "myService";
|
||||||
|
Target<MyService> target = mock(Target.class);
|
||||||
|
|
||||||
|
// mock return value
|
||||||
|
when(target.type()).thenReturn(targetType);
|
||||||
|
when(target.name()).thenReturn(name);
|
||||||
|
|
||||||
|
// Mock the fallback
|
||||||
|
MyService fallback = mock(MyService.class);
|
||||||
|
when(fallback.sayHello()).thenReturn("Fallback Hello");
|
||||||
|
|
||||||
|
// Call the target method
|
||||||
|
MyService result = builder.target(target, fallback);
|
||||||
|
|
||||||
|
// Verify that the result is not null and the fallback factory is used
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.sayHello()).isEqualTo("Fallback Hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTargetWithFallbackFactory() {
|
||||||
|
// Mock the target and fallback factory
|
||||||
|
Class<MyService> targetType = MyService.class;
|
||||||
|
String name = "myService";
|
||||||
|
Target<MyService> target = mock(Target.class);
|
||||||
|
|
||||||
|
// mock return value
|
||||||
|
when(target.type()).thenReturn(targetType);
|
||||||
|
when(target.name()).thenReturn(name);
|
||||||
|
|
||||||
|
FallbackFactory<MyService> fallbackFactory = mock(FallbackFactory.class);
|
||||||
|
|
||||||
|
// Mock the fallback from the factory
|
||||||
|
MyService fallback = mock(MyService.class);
|
||||||
|
when(fallback.sayHello()).thenReturn("Fallback Hello");
|
||||||
|
when(fallbackFactory.create(any())).thenReturn(fallback);
|
||||||
|
|
||||||
|
// Call the target method
|
||||||
|
MyService result = builder.target(target, fallbackFactory);
|
||||||
|
|
||||||
|
// Verify that the result is not null and the fallback factory is used
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.sayHello()).isEqualTo("Fallback Hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTargetWithoutFallback() {
|
||||||
|
// Mock the target
|
||||||
|
Class<MyService> targetType = MyService.class;
|
||||||
|
String name = "myService";
|
||||||
|
Target<MyService> target = mock(Target.class);
|
||||||
|
|
||||||
|
// mock return value
|
||||||
|
when(target.type()).thenReturn(targetType);
|
||||||
|
when(target.name()).thenReturn(name);
|
||||||
|
|
||||||
|
// Call the target method
|
||||||
|
MyService result = builder.target(target);
|
||||||
|
|
||||||
|
// Verify that the result is not null
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
// Additional verifications can be added here based on the implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildWithNullableFallbackFactory() {
|
||||||
|
// Call the build method with a null fallback factory
|
||||||
|
Feign feign = builder.build(null);
|
||||||
|
|
||||||
|
// Verify that the Feign instance is not null
|
||||||
|
assertThat(feign).isNotNull();
|
||||||
|
// Additional verifications can be added here based on the implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MyService {
|
||||||
|
@RequestLine("GET /hello")
|
||||||
|
String sayHello();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.polaris.config.adapter;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
|
||||||
|
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
|
||||||
|
import com.tencent.polaris.configuration.api.core.ConfigFileService;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||||
|
import org.springframework.cloud.context.refresh.ContextRefresher;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link PolarisRefreshEntireContextRefresher}.
|
||||||
|
*
|
||||||
|
* @author Shedfree Wu
|
||||||
|
*/
|
||||||
|
public class PolarisRefreshEntireContextRefresherTest {
|
||||||
|
@Mock
|
||||||
|
private PolarisConfigProperties polarisConfigProperties;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SpringValueRegistry springValueRegistry;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ConfigFileService configFileService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ContextRefresher contextRefresher;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ConfigurableApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PolarisConfigCustomExtensionLayer mockExtensionLayer;
|
||||||
|
|
||||||
|
private PolarisRefreshEntireContextRefresher refresher;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
refresher = new PolarisRefreshEntireContextRefresher(
|
||||||
|
polarisConfigProperties,
|
||||||
|
springValueRegistry,
|
||||||
|
configFileService,
|
||||||
|
contextRefresher
|
||||||
|
);
|
||||||
|
refresher.setApplicationContext(applicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRefreshSpringValue() {
|
||||||
|
// test refreshSpringValue method, it should do nothing
|
||||||
|
refresher.refreshSpringValue("test.key");
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verifyNoInteractions(contextRefresher);
|
||||||
|
verifyNoInteractions(springValueRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRefreshConfigurationPropertiesWithRefreshScope() {
|
||||||
|
// Arrange
|
||||||
|
Set<String> changeKeys = new HashSet<>();
|
||||||
|
changeKeys.add("test.key1");
|
||||||
|
changeKeys.add("test.key2");
|
||||||
|
|
||||||
|
// mock test.key1 in refresh scope
|
||||||
|
when(springValueRegistry.isRefreshScopeKey("test.key1")).thenReturn(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
refresher.refreshConfigurationProperties(changeKeys);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verify(contextRefresher, times(1)).refresh();
|
||||||
|
verifyNoInteractions(applicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRefreshConfigurationPropertiesWithoutRefreshScope() {
|
||||||
|
// Arrange
|
||||||
|
Set<String> changeKeys = new HashSet<>();
|
||||||
|
changeKeys.add("test.key1");
|
||||||
|
changeKeys.add("test.key2");
|
||||||
|
|
||||||
|
// mock a key not in refresh scope
|
||||||
|
when(springValueRegistry.isRefreshScopeKey(anyString())).thenReturn(false);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
refresher.refreshConfigurationProperties(changeKeys);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verify(contextRefresher, never()).refresh();
|
||||||
|
verify(applicationContext, times(1))
|
||||||
|
.publishEvent(any(EnvironmentChangeEvent.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSetApplicationContext() {
|
||||||
|
// Arrange
|
||||||
|
ConfigurableApplicationContext newContext = mock(ConfigurableApplicationContext.class);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
refresher.setApplicationContext(newContext);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
Set<String> changeKeys = new HashSet<>();
|
||||||
|
changeKeys.add("test.key");
|
||||||
|
when(springValueRegistry.isRefreshScopeKey(anyString())).thenReturn(false);
|
||||||
|
|
||||||
|
refresher.refreshConfigurationProperties(changeKeys);
|
||||||
|
verify(newContext, times(1)).publishEvent(any(EnvironmentChangeEvent.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRefreshConfigurationPropertiesWithEmptyChangeKeys() {
|
||||||
|
// Arrange
|
||||||
|
Set<String> changeKeys = new HashSet<>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
refresher.refreshConfigurationProperties(changeKeys);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verify(contextRefresher, never()).refresh();
|
||||||
|
verify(applicationContext, times(1))
|
||||||
|
.publishEvent(any(EnvironmentChangeEvent.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRefreshConfigurationPropertiesWithMultipleRefreshScopeKeys() {
|
||||||
|
// Arrange
|
||||||
|
Set<String> changeKeys = new HashSet<>();
|
||||||
|
changeKeys.add("test.key1");
|
||||||
|
changeKeys.add("test.key2");
|
||||||
|
changeKeys.add("test.key3");
|
||||||
|
|
||||||
|
// mock multiple keys in refresh scope
|
||||||
|
when(springValueRegistry.isRefreshScopeKey(anyString())).thenReturn(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
refresher.refreshConfigurationProperties(changeKeys);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verify(contextRefresher, times(1)).refresh();
|
||||||
|
verifyNoInteractions(applicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPolarisConfigCustomExtensionLayer() throws Exception {
|
||||||
|
refresher.setRegistered(true);
|
||||||
|
|
||||||
|
Field field = PolarisConfigPropertyAutoRefresher.class
|
||||||
|
.getDeclaredField("polarisConfigCustomExtensionLayer");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(refresher, mockExtensionLayer);
|
||||||
|
|
||||||
|
Method method = PolarisConfigPropertyAutoRefresher.class
|
||||||
|
.getDeclaredMethod("customInitRegisterPolarisConfig", PolarisConfigPropertyAutoRefresher.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(refresher, refresher);
|
||||||
|
|
||||||
|
|
||||||
|
method = PolarisConfigPropertyAutoRefresher.class.getDeclaredMethod(
|
||||||
|
"customRegisterPolarisConfigPublishChangeListener",
|
||||||
|
PolarisPropertySource.class, PolarisPropertySource.class);
|
||||||
|
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(refresher, null, null);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verify(mockExtensionLayer, times(1)).initRegisterConfig(refresher);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>spring-cloud-tencent-plugin-starters</artifactId>
|
||||||
|
<groupId>com.tencent.cloud</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>spring-cloud-tencent-security-protection-plugin</artifactId>
|
||||||
|
<name>Spring Cloud Tencent Lossless Plugin</name>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-beans</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webflux</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-inline</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.plugin.protection;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExitUtils.
|
||||||
|
*
|
||||||
|
* @author Shedfree Wu
|
||||||
|
*/
|
||||||
|
public final class ExitUtils {
|
||||||
|
|
||||||
|
private ExitUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exit(ApplicationContext context) {
|
||||||
|
exit(context, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exit(ApplicationContext context, int delay) {
|
||||||
|
if (context instanceof ConfigurableApplicationContext) {
|
||||||
|
ConfigurableApplicationContext configurableContext = (ConfigurableApplicationContext) context;
|
||||||
|
configurableContext.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(delay);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.plugin.protection;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.function.RouterFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SecurityProtectionAutoConfiguration.
|
||||||
|
*
|
||||||
|
* @author Shedfree Wu
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class SecurityProtectionAutoConfiguration {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityProtectionAutoConfiguration.class);
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnProperty(name = "spring.cloud.tencent.security.protection.servlet.enabled", matchIfMissing = true)
|
||||||
|
@ConditionalOnClass(name = {"org.springframework.web.servlet.function.RouterFunction"})
|
||||||
|
static class ServletProtectionConfiguration implements InitializingBean {
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
List<RouterFunction> routerFunctions;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
if (routerFunctions != null && !routerFunctions.isEmpty()) {
|
||||||
|
LOGGER.error("Detected the presence of webmvc RouterFunction-related beans, which may trigger the CVE-2024-38819 vulnerability. The program will soon exit.");
|
||||||
|
LOGGER.error("routerFunctions:{}: ", routerFunctions);
|
||||||
|
|
||||||
|
ExitUtils.exit(applicationContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnProperty(name = "spring.cloud.tencent.security.protection.reactive.enabled", matchIfMissing = true)
|
||||||
|
@ConditionalOnClass(name = {"org.springframework.web.reactive.function.server.RouterFunction"})
|
||||||
|
static class ReactiveProtectionConfiguration implements InitializingBean {
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
List<org.springframework.web.reactive.function.server.RouterFunction> routerFunctions;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
if (routerFunctions != null && !routerFunctions.isEmpty()) {
|
||||||
|
LOGGER.error("Detected the presence of webflux RouterFunction-related beans, which may trigger the CVE-2024-38819 vulnerability. The program will soon exit.");
|
||||||
|
LOGGER.error("routerFunctions:{}: ", routerFunctions);
|
||||||
|
ExitUtils.exit(applicationContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
# Auto Configuration
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
com.tencent.cloud.plugin.protection.SecurityProtectionAutoConfiguration
|
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.plugin.protection;
|
||||||
|
|
||||||
|
import java.security.Permission;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
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.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.web.servlet.function.RouterFunction;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link SecurityProtectionAutoConfiguration}.
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class SecurityProtectionAutoConfigurationTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ConfigurableApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testServletProtectionNoRouterFunctions() {
|
||||||
|
// Arrange
|
||||||
|
SecurityProtectionAutoConfiguration.ServletProtectionConfiguration config =
|
||||||
|
new SecurityProtectionAutoConfiguration.ServletProtectionConfiguration();
|
||||||
|
config.applicationContext = applicationContext;
|
||||||
|
config.routerFunctions = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
config.afterPropertiesSet();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
// Should not call exit when no RouterFunctions present
|
||||||
|
verify(applicationContext, never()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testServletProtectionEmptyRouterFunctions() {
|
||||||
|
// Arrange
|
||||||
|
SecurityProtectionAutoConfiguration.ServletProtectionConfiguration config =
|
||||||
|
new SecurityProtectionAutoConfiguration.ServletProtectionConfiguration();
|
||||||
|
config.applicationContext = applicationContext;
|
||||||
|
config.routerFunctions = new ArrayList<>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
config.afterPropertiesSet();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
// Should not call exit when RouterFunctions list is empty
|
||||||
|
verify(applicationContext, never()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testServletProtectionWithRouterFunctions() {
|
||||||
|
// Arrange
|
||||||
|
SecurityProtectionAutoConfiguration.ServletProtectionConfiguration config =
|
||||||
|
new SecurityProtectionAutoConfiguration.ServletProtectionConfiguration();
|
||||||
|
config.applicationContext = mock(ConfigurableApplicationContext.class);
|
||||||
|
List<RouterFunction> routerFunctions = new ArrayList<>();
|
||||||
|
routerFunctions.add(mock(RouterFunction.class));
|
||||||
|
config.routerFunctions = routerFunctions;
|
||||||
|
|
||||||
|
SecurityManager originalSecurityManager = System.getSecurityManager();
|
||||||
|
System.setSecurityManager(new ExitSecurityManager());
|
||||||
|
|
||||||
|
try {
|
||||||
|
config.afterPropertiesSet();
|
||||||
|
}
|
||||||
|
catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setSecurityManager(originalSecurityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReactiveProtectionNoRouterFunctions() {
|
||||||
|
// Arrange
|
||||||
|
SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration config =
|
||||||
|
new SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration();
|
||||||
|
config.applicationContext = applicationContext;
|
||||||
|
config.routerFunctions = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
config.afterPropertiesSet();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verify(applicationContext, never()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReactiveProtectionEmptyRouterFunctions() {
|
||||||
|
// Arrange
|
||||||
|
SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration config =
|
||||||
|
new SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration();
|
||||||
|
config.applicationContext = applicationContext;
|
||||||
|
config.routerFunctions = new ArrayList<>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
config.afterPropertiesSet();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
verify(applicationContext, never()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReactiveProtectionWithRouterFunctions() {
|
||||||
|
// Arrange
|
||||||
|
SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration config =
|
||||||
|
new SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration();
|
||||||
|
config.applicationContext = mock(ConfigurableApplicationContext.class);
|
||||||
|
List<org.springframework.web.reactive.function.server.RouterFunction> routerFunctions = new ArrayList<>();
|
||||||
|
routerFunctions.add(mock(org.springframework.web.reactive.function.server.RouterFunction.class));
|
||||||
|
config.routerFunctions = routerFunctions;
|
||||||
|
|
||||||
|
SecurityManager originalSecurityManager = System.getSecurityManager();
|
||||||
|
System.setSecurityManager(new ExitSecurityManager());
|
||||||
|
|
||||||
|
try {
|
||||||
|
config.afterPropertiesSet();
|
||||||
|
}
|
||||||
|
catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setSecurityManager(originalSecurityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInterruptedExceptionHandling() throws InterruptedException {
|
||||||
|
// Arrange
|
||||||
|
ConfigurableApplicationContext mockContext = mock(ConfigurableApplicationContext.class);
|
||||||
|
Thread testThread = new Thread(() -> ExitUtils.exit(mockContext, 3000));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SecurityManager originalSecurityManager = System.getSecurityManager();
|
||||||
|
System.setSecurityManager(new ExitSecurityManager());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Act
|
||||||
|
testThread.start();
|
||||||
|
testThread.interrupt();
|
||||||
|
Thread.sleep(6000);
|
||||||
|
}
|
||||||
|
catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setSecurityManager(originalSecurityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExitSecurityManager extends SecurityManager {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkPermission(Permission perm) {
|
||||||
|
if (perm.getName().contains("exitVM")) {
|
||||||
|
throw new SecurityException("System.exit is not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.polaris.context.listener;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Failed event listener.
|
||||||
|
*
|
||||||
|
* @author skyehtzhang
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class FailedEventApplicationListener implements ApplicationListener<ApplicationEvent>, ApplicationContextAware {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FailedEventApplicationListener.class);
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ApplicationEvent event) {
|
||||||
|
if (event instanceof ApplicationFailedEvent) {
|
||||||
|
ApplicationFailedEvent failedEvent = (ApplicationFailedEvent) event;
|
||||||
|
if (failedEvent.getException() != null) {
|
||||||
|
logger.error("[onApplicationEvent] exception in failed event", failedEvent.getException());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applicationContext instanceof ConfigurableApplicationContext) {
|
||||||
|
((ConfigurableApplicationContext) applicationContext).close();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(3000);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.polaris.context.listener;
|
||||||
|
|
||||||
|
import java.security.Permission;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
|
||||||
|
class FailedEventApplicationListenerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ConfigurableApplicationContext mockConfigurableContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ApplicationContext mockApplicationContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ApplicationFailedEvent mockFailedEvent;
|
||||||
|
|
||||||
|
private FailedEventApplicationListener listener;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
listener = new FailedEventApplicationListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSetApplicationContext() {
|
||||||
|
// Test setting application context
|
||||||
|
listener.setApplicationContext(mockApplicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnApplicationEventWithConfigurableContext() {
|
||||||
|
// Arrange
|
||||||
|
listener.setApplicationContext(mockConfigurableContext);
|
||||||
|
when(mockFailedEvent.getException()).thenReturn(new RuntimeException("Test Exception"));
|
||||||
|
|
||||||
|
SecurityManager originalSecurityManager = System.getSecurityManager();
|
||||||
|
System.setSecurityManager(new ExitSecurityManager());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Act
|
||||||
|
listener.onApplicationEvent(mockFailedEvent);
|
||||||
|
}
|
||||||
|
catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setSecurityManager(originalSecurityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnApplicationEventWithNonConfigurableContext() {
|
||||||
|
// Arrange
|
||||||
|
listener.setApplicationContext(mockApplicationContext);
|
||||||
|
when(mockFailedEvent.getException()).thenReturn(new RuntimeException("Test Exception"));
|
||||||
|
|
||||||
|
SecurityManager originalSecurityManager = System.getSecurityManager();
|
||||||
|
System.setSecurityManager(new ExitSecurityManager());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Act
|
||||||
|
listener.onApplicationEvent(mockFailedEvent);
|
||||||
|
}
|
||||||
|
catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setSecurityManager(originalSecurityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnApplicationEventWithInterruptedException() {
|
||||||
|
// Arrange
|
||||||
|
listener.setApplicationContext(mockConfigurableContext);
|
||||||
|
Thread.currentThread().interrupt(); // Simulate interruption
|
||||||
|
|
||||||
|
|
||||||
|
SecurityManager originalSecurityManager = System.getSecurityManager();
|
||||||
|
System.setSecurityManager(new ExitSecurityManager());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Act
|
||||||
|
listener.onApplicationEvent(mockFailedEvent);
|
||||||
|
}
|
||||||
|
catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setSecurityManager(originalSecurityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOnApplicationEventWithNullException() {
|
||||||
|
// Arrange
|
||||||
|
listener.setApplicationContext(mockConfigurableContext);
|
||||||
|
when(mockFailedEvent.getException()).thenReturn(null);
|
||||||
|
|
||||||
|
|
||||||
|
SecurityManager originalSecurityManager = System.getSecurityManager();
|
||||||
|
System.setSecurityManager(new ExitSecurityManager());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Act
|
||||||
|
listener.onApplicationEvent(mockFailedEvent);
|
||||||
|
}
|
||||||
|
catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setSecurityManager(originalSecurityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExitSecurityManager extends SecurityManager {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkPermission(Permission perm) {
|
||||||
|
if (perm.getName().contains("exitVM")) {
|
||||||
|
throw new SecurityException("System.exit is not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the BSD 3-Clause License (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tencent.cloud.rpc.enhancement.plugin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DefaultEnhancedPluginRunner}.
|
||||||
|
*
|
||||||
|
* @author Shedfree Wu
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class DefaultEnhancedPluginRunnerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPolarisRegistration_WithPolarisRegistration_ReturnsPolarisRegistration() {
|
||||||
|
// Arrange
|
||||||
|
List<Registration> registrations = new ArrayList<>();
|
||||||
|
Registration normalReg = createMockRegistration();
|
||||||
|
Registration normalReg2 = createMockRegistration();
|
||||||
|
registrations.add(normalReg);
|
||||||
|
registrations.add(normalReg2);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Registration result = DefaultEnhancedPluginRunner.getPolarisRegistration(registrations);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result).isSameAs(normalReg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to create mock Registration objects
|
||||||
|
private Registration createMockRegistration() {
|
||||||
|
return new Registration() {
|
||||||
|
@Override
|
||||||
|
public String getServiceId() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHost() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPort() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecure() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.net.URI getUri() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.util.Map<String, String> getMetadata() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in new issue