diff --git a/.github/workflows/junit_test.yml b/.github/workflows/junit_test.yml
index 5738a4508..4e397fa2b 100644
--- a/.github/workflows/junit_test.yml
+++ b/.github/workflows/junit_test.yml
@@ -26,3 +26,7 @@ jobs:
# run: mvn -B package --file pom.xml
- name: Test with Maven
run: mvn -B test --file pom.xml
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v1
+ with:
+ file: ${{ github.workspace }}/target/site/jacoco/jacoco.xml
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java
index e72393161..33231b2ff 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java
@@ -24,8 +24,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.client.ServiceInstance;
@@ -41,8 +40,7 @@ import static org.mockito.Mockito.when;
*
* @author Haotian Zhang
*/
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore("javax.management.*")
+@RunWith(MockitoJUnitRunner.class)
public class PolarisDiscoveryClientTest {
@Mock
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java
index 300a94cd9..88c34028a 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java
@@ -25,8 +25,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
@@ -41,8 +40,7 @@ import static org.mockito.Mockito.when;
*
* @author Haotian Zhang
*/
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore("javax.management.*")
+@RunWith(MockitoJUnitRunner.class)
public class PolarisReactiveDiscoveryClientTest {
@Mock
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml b/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml
index fe07a8d90..aaccef814 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml
@@ -91,14 +91,20 @@
- org.powermock
- powermock-module-junit4
+ org.mockito
+ mockito-inline
test
- org.powermock
- powermock-api-mockito2
+ org.mockito
+ mockito-core
+ test
+
+
+
+ net.bytebuddy
+ byte-buddy
test
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java
index ae8774386..3bae2e3bd 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.Map;
import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.ExpressionLabelUtils;
import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver;
import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
@@ -33,14 +34,14 @@ import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -49,7 +50,6 @@ 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 org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
@@ -58,19 +58,16 @@ import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
/**
* Test for {@link QuotaCheckReactiveFilter}.
*
* @author Haotian Zhang
*/
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"javax.management.*", "javax.script.*"})
-@PowerMockRunnerDelegate(SpringRunner.class)
-@PrepareForTest(ExpressionLabelUtils.class)
+@RunWith(MockitoJUnitRunner.class)
@SpringBootTest(classes = QuotaCheckReactiveFilterTest.TestApplication.class, properties = {
"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"
})
@@ -80,11 +77,23 @@ public class QuotaCheckReactiveFilterTest {
private QuotaCheckReactiveFilter quotaCheckReactiveFilter;
+ private static MockedStatic mockedApplicationContextAwareUtils;
+ private static MockedStatic expressionLabelUtilsMockedStatic;
+
@BeforeClass
public static void beforeClass() {
- // mock ExpressionLabelUtils.resolve()
- mockStatic(ExpressionLabelUtils.class);
+ expressionLabelUtilsMockedStatic = mockStatic(ExpressionLabelUtils.class);
when(ExpressionLabelUtils.resolve(any(ServerWebExchange.class), anySet())).thenReturn(Collections.singletonMap("RuleLabelResolver", "RuleLabelResolver"));
+
+ mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn("unit-test");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ mockedApplicationContextAwareUtils.close();
+ expressionLabelUtilsMockedStatic.close();
}
@Before
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java
index b9cf5cb67..a2cc344dc 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java
@@ -29,6 +29,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.ExpressionLabelUtils;
import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver;
import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
@@ -37,20 +38,19 @@ import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
@@ -58,19 +58,16 @@ import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
/**
* Test for {@link QuotaCheckServletFilter}.
*
* @author Haotian Zhang
*/
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"javax.management.*", "javax.script.*"})
-@PowerMockRunnerDelegate(SpringRunner.class)
-@PrepareForTest(ExpressionLabelUtils.class)
+@RunWith(MockitoJUnitRunner.class)
@SpringBootTest(classes = QuotaCheckServletFilterTest.TestApplication.class, properties = {
"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"
})
@@ -80,11 +77,23 @@ public class QuotaCheckServletFilterTest {
private QuotaCheckServletFilter quotaCheckServletFilter;
+ private static MockedStatic mockedApplicationContextAwareUtils;
+ private static MockedStatic expressionLabelUtilsMockedStatic;
@BeforeClass
public static void beforeClass() {
- // mock ExpressionLabelUtils.resolve()
- mockStatic(ExpressionLabelUtils.class);
+ expressionLabelUtilsMockedStatic = mockStatic(ExpressionLabelUtils.class);
when(ExpressionLabelUtils.resolve(any(ServerWebExchange.class), anySet())).thenReturn(Collections.singletonMap("RuleLabelResolver", "RuleLabelResolver"));
+
+ mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn("unit-test");
+
+ }
+
+ @AfterClass
+ public static void afterClass() throws Exception {
+ mockedApplicationContextAwareUtils.close();
+ expressionLabelUtilsMockedStatic.close();
}
@Before
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java
index 8589a2b6b..d679381cb 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java
@@ -25,21 +25,19 @@ import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* Test for {@link QuotaCheckUtils}.
*
* @author Haotian Zhang
*/
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"javax.management.*", "javax.script.*"})
+@RunWith(MockitoJUnitRunner.class)
public class QuotaCheckUtilsTest {
private LimitAPI limitAPI;
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtilsTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtilsTest.java
index 5eb7901df..351e0b737 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtilsTest.java
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtilsTest.java
@@ -24,24 +24,20 @@ import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.QUOTA_LIMITED_INFO;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
/**
* Test for {@link RateLimitUtils}.
*
* @author Haotian Zhang
*/
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"javax.management.*", "javax.script.*"})
-@PrepareForTest(ResourceFileUtils.class)
+@RunWith(MockitoJUnitRunner.class)
public class RateLimitUtilsTest {
@BeforeClass
diff --git a/spring-cloud-starter-tencent-polaris-router/pom.xml b/spring-cloud-starter-tencent-polaris-router/pom.xml
index 3aa7ecc12..91e482abb 100644
--- a/spring-cloud-starter-tencent-polaris-router/pom.xml
+++ b/spring-cloud-starter-tencent-polaris-router/pom.xml
@@ -52,6 +52,30 @@
true
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.mockito
+ mockito-inline
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ net.bytebuddy
+ byte-buddy
+ test
+
+
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java
index 89e87e18d..0c9b285d4 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java
@@ -71,14 +71,14 @@ import org.springframework.util.CollectionUtils;
*/
public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
- private final static String STRATEGY_RANDOM = "random";
- private final static String STRATEGY_ROUND_ROBIN = "roundRobin";
- private final static String STRATEGY_WEIGHT = "polarisWeighted";
- private final static String STRATEGY_RETRY = "retry";
- private final static String STRATEGY_RESPONSE_TIME_WEIGHTED = "responseTimeWeighted";
- private final static String STRATEGY_BEST_AVAILABLE = "bestAvailable";
- private final static String STRATEGY_ZONE_AVOIDANCE = "zoneAvoidance";
- private final static String STRATEGY_AVAILABILITY_FILTERING = "availabilityFilteringRule";
+ final static String STRATEGY_RANDOM = "random";
+ final static String STRATEGY_ROUND_ROBIN = "roundRobin";
+ final static String STRATEGY_WEIGHT = "polarisWeighted";
+ final static String STRATEGY_RETRY = "retry";
+ final static String STRATEGY_RESPONSE_TIME_WEIGHTED = "responseTimeWeighted";
+ final static String STRATEGY_BEST_AVAILABLE = "bestAvailable";
+ final static String STRATEGY_ZONE_AVOIDANCE = "zoneAvoidance";
+ final static String STRATEGY_AVAILABILITY_FILTERING = "availabilityFilteringRule";
private final PolarisLoadBalancerProperties loadBalancerProperties;
private final PolarisNearByRouterProperties polarisNearByRouterProperties;
@@ -129,7 +129,7 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
return delegateRule.choose(key);
}
- private List doRouter(List allServers, Object key) {
+ List doRouter(List allServers, Object key) {
ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(allServers);
// filter instance by routers
@@ -145,7 +145,7 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
return filteredInstances;
}
- private ProcessRoutersRequest buildProcessRoutersRequest(ServiceInstances serviceInstances, Object key) {
+ ProcessRoutersRequest buildProcessRoutersRequest(ServiceInstances serviceInstances, Object key) {
ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest();
processRoutersRequest.setDstInstances(serviceInstances);
@@ -195,9 +195,6 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
public AbstractLoadBalancerRule getRule() {
String loadBalanceStrategy = loadBalancerProperties.getStrategy();
- if (org.springframework.util.StringUtils.isEmpty(loadBalanceStrategy)) {
- return new RoundRobinRule();
- }
switch (loadBalanceStrategy) {
case STRATEGY_RANDOM:
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java
index ebcc8fd42..114548722 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java
@@ -46,7 +46,11 @@ public class PolarisRouterContext {
if (CollectionUtils.isEmpty(labels)) {
return Collections.emptyMap();
}
- return Collections.unmodifiableMap(labels.get(labelType));
+ Map subLabels = labels.get(labelType);
+ if (CollectionUtils.isEmpty(subLabels)) {
+ return Collections.emptyMap();
+ }
+ return Collections.unmodifiableMap(subLabels);
}
public void setLabels(String labelType, Map subLabels) {
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java
index 77266cc02..6437c83e8 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java
@@ -28,5 +28,5 @@ public class RouterConstants {
/**
* the header of router label.
*/
- public static final String ROUTER_LABEL_HEADER = "router-label";
+ public static final String ROUTER_LABEL_HEADER = "internal-router-label";
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java
index c8ab04c3c..d509c1bbf 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java
@@ -18,6 +18,7 @@
package com.tencent.cloud.polaris.router;
+import java.util.Collections;
import java.util.List;
import com.netflix.loadbalancer.ILoadBalancer;
@@ -48,16 +49,25 @@ public class SimpleLoadBalancer implements ILoadBalancer {
@Override
public List getServerList(boolean availableOnly) {
- return servers;
+ if (servers == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(servers);
}
@Override
public List getReachableServers() {
- return servers;
+ if (servers == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(servers);
}
@Override
public List getAllServers() {
- return servers;
+ if (servers == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(servers);
}
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtils.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtils.java
index cf4408d37..92b8a6608 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtils.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtils.java
@@ -18,6 +18,7 @@
package com.tencent.cloud.polaris.router.feign;
+import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -62,7 +63,8 @@ public class FeignExpressionLabelUtils {
labels.put(labelKey, request.method());
}
else if (StringUtils.equalsIgnoreCase(ExpressionLabelUtils.LABEL_URI, labelKey)) {
- labels.put(labelKey, request.request().url());
+ URI uri = URI.create(request.request().url());
+ labels.put(labelKey, uri.getPath());
}
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java
index 5a13e219f..e46b17498 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java
@@ -51,11 +51,18 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
protected void customizeLoadBalancerCommandBuilder(RibbonRequest request, IClientConfig config,
LoadBalancerCommand.Builder builder) {
Map> headers = request.getRequest().headers();
+
+ PolarisRouterContext routerContext = buildRouterContext(headers);
+
+ builder.withServerLocator(routerContext);
+ }
+
+ //set method to public for unit test
+ PolarisRouterContext buildRouterContext(Map> headers) {
Collection labelHeaderValues = headers.get(RouterConstants.ROUTER_LABEL_HEADER);
if (CollectionUtils.isEmpty(labelHeaderValues)) {
- builder.withServerLocator(null);
- return;
+ return null;
}
PolarisRouterContext routerContext = new PolarisRouterContext();
@@ -76,6 +83,6 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
}
});
- builder.withServerLocator(routerContext);
+ return routerContext;
}
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java
index ef4ac450f..fac0879b4 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java
@@ -74,12 +74,13 @@ public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered
@Override
public void apply(RequestTemplate requestTemplate) {
- Map labels = new HashMap<>();
+ // local service labels
+ Map labels = new HashMap<>(metadataLocalProperties.getContent());
- // labels from downstream
- Map transitiveLabels = MetadataContextHolder.get()
- .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
- labels.putAll(transitiveLabels);
+ // labels from rule expression
+ String peerServiceName = requestTemplate.feignTarget().name();
+ Map ruleExpressionLabels = getRuleExpressionLabels(requestTemplate, peerServiceName);
+ labels.putAll(ruleExpressionLabels);
// labels from request
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
@@ -96,14 +97,10 @@ public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered
});
}
- // labels from rule expression
- String peerServiceName = requestTemplate.feignTarget().name();
- Map ruleExpressionLabels = getRuleExpressionLabels(requestTemplate, peerServiceName);
- labels.putAll(ruleExpressionLabels);
-
-
- //local service labels
- labels.putAll(metadataLocalProperties.getContent());
+ // labels from downstream
+ Map transitiveLabels = MetadataContextHolder.get()
+ .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
+ labels.putAll(transitiveLabels);
// Because when the label is placed in RequestTemplate.header,
// RequestTemplate will parse the header according to the regular, which conflicts with the expression.
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java
index 938652497..de474c3f1 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java
@@ -105,13 +105,15 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
this.requestFactory.createRequest(request, body, execution));
}
- private PolarisRouterContext genRouterContext(HttpRequest request, byte[] body, String peerServiceName) {
- Map labels = new HashMap<>();
+ PolarisRouterContext genRouterContext(HttpRequest request, byte[] body, String peerServiceName) {
+ // local service labels
+ Map labels = new HashMap<>(metadataLocalProperties.getContent());
- // labels from downstream
- Map transitiveLabels = MetadataContextHolder.get()
- .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
- labels.putAll(transitiveLabels);
+ // labels from rule expression
+ Map ruleExpressionLabels = getExpressionLabels(request, peerServiceName);
+ if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
+ labels.putAll(ruleExpressionLabels);
+ }
// labels from request
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
@@ -128,14 +130,10 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
});
}
- // labels from rule expression
- Map ruleExpressionLabels = getExpressionLabels(request, peerServiceName);
- if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
- labels.putAll(ruleExpressionLabels);
- }
-
- // local service labels
- labels.putAll(metadataLocalProperties.getContent());
+ // labels from downstream
+ Map transitiveLabels = MetadataContextHolder.get()
+ .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
+ labels.putAll(transitiveLabels);
PolarisRouterContext routerContext = new PolarisRouterContext();
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRuleTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRuleTest.java
new file mode 100644
index 000000000..1eccb542c
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRuleTest.java
@@ -0,0 +1,362 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.netflix.client.config.DefaultClientConfigImpl;
+import com.netflix.client.config.IClientConfig;
+import com.netflix.loadbalancer.AbstractLoadBalancerRule;
+import com.netflix.loadbalancer.AvailabilityFilteringRule;
+import com.netflix.loadbalancer.BestAvailableRule;
+import com.netflix.loadbalancer.RandomRule;
+import com.netflix.loadbalancer.RetryRule;
+import com.netflix.loadbalancer.RoundRobinRule;
+import com.netflix.loadbalancer.Server;
+import com.netflix.loadbalancer.WeightedResponseTimeRule;
+import com.netflix.loadbalancer.ZoneAvoidanceRule;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.pojo.PolarisServer;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.cloud.polaris.loadbalancer.PolarisWeightedRule;
+import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties;
+import com.tencent.cloud.polaris.router.config.PolarisMetadataRouterProperties;
+import com.tencent.cloud.polaris.router.config.PolarisNearByRouterProperties;
+import com.tencent.cloud.polaris.router.config.PolarisRuleBasedRouterProperties;
+import com.tencent.polaris.api.pojo.DefaultInstance;
+import com.tencent.polaris.api.pojo.DefaultServiceInstances;
+import com.tencent.polaris.api.pojo.Instance;
+import com.tencent.polaris.api.pojo.ServiceInstances;
+import com.tencent.polaris.api.pojo.ServiceKey;
+import com.tencent.polaris.plugins.router.metadata.MetadataRouter;
+import com.tencent.polaris.plugins.router.nearby.NearbyRouter;
+import com.tencent.polaris.plugins.router.rule.RuleBasedRouter;
+import com.tencent.polaris.router.api.core.RouterAPI;
+import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
+import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+/**
+ * test for {@link PolarisLoadBalancerCompositeRule}
+ *@author lepdou 2022-05-26
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class PolarisLoadBalancerCompositeRuleTest {
+
+ @Mock
+ private PolarisLoadBalancerProperties polarisLoadBalancerProperties;
+ @Mock
+ private PolarisNearByRouterProperties polarisNearByRouterProperties;
+ @Mock
+ private PolarisMetadataRouterProperties polarisMetadataRouterProperties;
+ @Mock
+ private PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties;
+ @Mock
+ private RouterAPI routerAPI;
+
+ private IClientConfig config;
+
+ private static AtomicBoolean initTransitiveMetadata = new AtomicBoolean(false);
+
+ private String testNamespace = "testNamespace";
+ private String testCallerService = "testCallerService";
+ private String testCalleeService = "testCalleeService";
+
+ @Before
+ public void before() {
+ config = new DefaultClientConfigImpl();
+ config.loadDefaultValues();
+ }
+
+ @Test
+ public void testGetDefaultLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn("");
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule defaultRule = compositeRule.getRule();
+
+ Assert.assertTrue(defaultRule instanceof ZoneAvoidanceRule);
+ }
+
+ @Test
+ public void testRandomLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_RANDOM);
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule lbRule = compositeRule.getRule();
+
+ Assert.assertTrue(lbRule instanceof RandomRule);
+ }
+
+ @Test
+ public void testWeightLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_WEIGHT);
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule lbRule = compositeRule.getRule();
+
+ Assert.assertTrue(lbRule instanceof PolarisWeightedRule);
+ }
+
+ @Test
+ public void testRetryLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_RETRY);
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule lbRule = compositeRule.getRule();
+
+ Assert.assertTrue(lbRule instanceof RetryRule);
+ }
+
+ @Test
+ public void testWeightedResponseTimeLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_RESPONSE_TIME_WEIGHTED);
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule lbRule = compositeRule.getRule();
+
+ Assert.assertTrue(lbRule instanceof WeightedResponseTimeRule);
+ }
+
+ @Test
+ public void tesBestAvailableLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_BEST_AVAILABLE);
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule lbRule = compositeRule.getRule();
+
+ Assert.assertTrue(lbRule instanceof BestAvailableRule);
+ }
+
+ @Test
+ public void tesRoundRobinLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_ROUND_ROBIN);
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule lbRule = compositeRule.getRule();
+
+ Assert.assertTrue(lbRule instanceof RoundRobinRule);
+ }
+
+ @Test
+ public void testAvailabilityFilteringLB() {
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_AVAILABILITY_FILTERING);
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ AbstractLoadBalancerRule lbRule = compositeRule.getRule();
+
+ Assert.assertTrue(lbRule instanceof AvailabilityFilteringRule);
+ }
+
+ @Test
+ public void testBuildMetadataRouteRequest() {
+ when(polarisMetadataRouterProperties.isEnabled()).thenReturn(true);
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn("");
+
+ try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn(testCallerService);
+
+ setTransitiveMetadata();
+
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ ServiceInstances serviceInstances = assembleServiceInstances();
+ PolarisRouterContext routerContext = assembleRouterContext();
+
+ ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext);
+
+ Map routerMetadata = request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA);
+
+ Assert.assertEquals(1, routerMetadata.size());
+ Assert.assertEquals(0, request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY).size());
+ Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size());
+ Assert.assertEquals("false", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)
+ .get(RuleBasedRouter.ROUTER_ENABLED));
+ }
+ }
+
+ @Test
+ public void testBuildNearbyRouteRequest() {
+ when(polarisNearByRouterProperties.isEnabled()).thenReturn(true);
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn("");
+
+ try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn(testCallerService);
+
+ setTransitiveMetadata();
+
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ ServiceInstances serviceInstances = assembleServiceInstances();
+ PolarisRouterContext routerContext = assembleRouterContext();
+
+ ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext);
+
+ Map routerMetadata = request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY);
+
+ Assert.assertEquals(0, request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA).size());
+ Assert.assertEquals(1, routerMetadata.size());
+ Assert.assertEquals("true", routerMetadata.get(NearbyRouter.ROUTER_ENABLED));
+ Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size());
+ Assert.assertEquals("false", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)
+ .get(RuleBasedRouter.ROUTER_ENABLED));
+ }
+ }
+
+ @Test
+ public void testBuildRuleBasedRouteRequest() {
+ when(polarisRuleBasedRouterProperties.isEnabled()).thenReturn(true);
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn("");
+
+ try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())).
+ thenReturn(testCallerService);
+
+ setTransitiveMetadata();
+
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ ServiceInstances serviceInstances = assembleServiceInstances();
+ PolarisRouterContext routerContext = assembleRouterContext();
+
+ ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext);
+
+ Map routerMetadata = request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED);
+
+ Assert.assertEquals(1, routerMetadata.size());
+ Assert.assertEquals(0, request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA).size());
+ Assert.assertEquals(0, request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY).size());
+ Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size());
+ Assert.assertEquals("true", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)
+ .get(RuleBasedRouter.ROUTER_ENABLED));
+ }
+ }
+
+ @Test
+ public void testRouter() {
+ when(polarisRuleBasedRouterProperties.isEnabled()).thenReturn(true);
+ when(polarisLoadBalancerProperties.getStrategy()).thenReturn("");
+
+ try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn(testCallerService);
+
+ setTransitiveMetadata();
+
+ PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI,
+ polarisLoadBalancerProperties, polarisNearByRouterProperties,
+ polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config);
+
+ ProcessRoutersResponse assembleResponse = assembleProcessRoutersResponse();
+ when(routerAPI.processRouters(any())).thenReturn(assembleResponse);
+
+ List servers = compositeRule.doRouter(assembleServers(), assembleRouterContext());
+
+ Assert.assertEquals(assembleResponse.getServiceInstances().getInstances().size(), servers.size());
+ }
+ }
+
+ private void setTransitiveMetadata() {
+ if (initTransitiveMetadata.compareAndSet(false, true)) {
+ // mock transitive metadata
+ MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
+ try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
+ mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+ }
+ }
+ }
+
+ private ServiceInstances assembleServiceInstances() {
+ ServiceKey serviceKey = new ServiceKey(testNamespace, testCalleeService);
+ List instances = new LinkedList<>();
+ instances.add(new DefaultInstance());
+ instances.add(new DefaultInstance());
+ instances.add(new DefaultInstance());
+ instances.add(new DefaultInstance());
+ instances.add(new DefaultInstance());
+
+ return new DefaultServiceInstances(serviceKey, instances);
+ }
+
+ private PolarisRouterContext assembleRouterContext() {
+ PolarisRouterContext routerContext = new PolarisRouterContext();
+ Map transitiveLabels = new HashMap<>();
+ transitiveLabels.put("k1", "v1");
+ Map routerLabels = new HashMap<>();
+ routerLabels.put("k2", "v2");
+ routerLabels.put("k3", "v3");
+ routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels);
+ routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, routerLabels);
+ return routerContext;
+ }
+
+ private ProcessRoutersResponse assembleProcessRoutersResponse() {
+ return new ProcessRoutersResponse(assembleServiceInstances());
+ }
+
+ private List assembleServers() {
+ ServiceInstances serviceInstances = assembleServiceInstances();
+ List servers = new LinkedList<>();
+ servers.add(new PolarisServer(serviceInstances, new DefaultInstance()));
+ servers.add(new PolarisServer(serviceInstances, new DefaultInstance()));
+ servers.add(new PolarisServer(serviceInstances, new DefaultInstance()));
+ servers.add(new PolarisServer(serviceInstances, new DefaultInstance()));
+ return servers;
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisRouterContextTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisRouterContextTest.java
new file mode 100644
index 000000000..2a8d38752
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisRouterContextTest.java
@@ -0,0 +1,64 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * test for {@link PolarisRouterContext}
+ *
+ *@author lepdou 2022-05-26
+ */
+public class PolarisRouterContextTest {
+
+ @Test
+ public void testNormalGetterSetter() {
+ Map labels = new HashMap<>();
+ labels.put("k1", "v1");
+ labels.put("k2", "v2");
+
+ PolarisRouterContext routerContext = new PolarisRouterContext();
+ routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels);
+
+ Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size());
+ Assert.assertEquals(2, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size());
+ Assert.assertEquals("v1", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k1"));
+ Assert.assertEquals("v2", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k2"));
+ Assert.assertNull(routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k3"));
+ }
+
+ @Test
+ public void testSetNull() {
+ PolarisRouterContext routerContext = new PolarisRouterContext();
+ routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, null);
+ Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size());
+ Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size());
+ }
+
+ @Test
+ public void testGetEmptyRouterContext() {
+ PolarisRouterContext routerContext = new PolarisRouterContext();
+ Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size());
+ Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size());
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolverTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolverTest.java
new file mode 100644
index 000000000..26c1dfb13
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolverTest.java
@@ -0,0 +1,93 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router;
+
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.Lists;
+import com.tencent.cloud.polaris.context.ServiceRuleManager;
+import com.tencent.polaris.client.pb.ModelProto;
+import com.tencent.polaris.client.pb.RoutingProto;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.mockito.Mockito.when;
+
+/**
+ * test for {@link RouterRuleLabelResolver}
+ *@author lepdou 2022-05-26
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class RouterRuleLabelResolverTest {
+
+ @Mock
+ private ServiceRuleManager serviceRuleManager;
+
+ private final String testNamespace = "testNamespace";
+ private final String testSourceService = "sourceService";
+ private final String testDstService = "dstService";
+
+ @Test
+ public void test() {
+ Map labels = new HashMap<>();
+ ModelProto.MatchString matchString = ModelProto.MatchString.getDefaultInstance();
+ String validKey1 = "${http.header.uid}";
+ String validKey2 = "${http.query.name}";
+ String validKey3 = "${http.method}";
+ String validKey4 = "${http.uri}";
+ String invalidKey = "${http.expression.wrong}";
+ labels.put(validKey1, matchString);
+ labels.put(validKey2, matchString);
+ labels.put(validKey3, matchString);
+ labels.put(validKey4, matchString);
+ labels.put(invalidKey, matchString);
+
+ RoutingProto.Source source1 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build();
+ RoutingProto.Source source2 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build();
+ RoutingProto.Source source3 = RoutingProto.Source.newBuilder().putAllMetadata(new HashMap<>()).build();
+
+ List routes = new LinkedList<>();
+ RoutingProto.Route route = RoutingProto.Route.newBuilder()
+ .addAllSources(Lists.newArrayList(source1, source2, source3))
+ .build();
+ routes.add(route);
+
+ when(serviceRuleManager.getServiceRouterRule(testNamespace, testSourceService, testDstService)).thenReturn(routes);
+
+ RouterRuleLabelResolver resolver = new RouterRuleLabelResolver(serviceRuleManager);
+
+ Set resolvedExpressionLabelKeys = resolver.getExpressionLabelKeys(testNamespace, testSourceService, testDstService);
+
+ Assert.assertNotNull(resolvedExpressionLabelKeys);
+ Assert.assertEquals(4, resolvedExpressionLabelKeys.size());
+ Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey1));
+ Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey2));
+ Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey3));
+ Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey4));
+ Assert.assertFalse(resolvedExpressionLabelKeys.contains(invalidKey));
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/SimpleLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/SimpleLoadBalancerTest.java
new file mode 100644
index 000000000..36e512b4c
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/SimpleLoadBalancerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import com.netflix.loadbalancer.Server;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * test for {@link SimpleLoadBalancer}
+ *@author lepdou 2022-05-26
+ */
+public class SimpleLoadBalancerTest {
+
+ @Test
+ public void testSetterGetter() {
+ List servers = new LinkedList<>();
+ servers.add(Mockito.mock(Server.class));
+ servers.add(Mockito.mock(Server.class));
+ servers.add(Mockito.mock(Server.class));
+ servers.add(Mockito.mock(Server.class));
+ servers.add(Mockito.mock(Server.class));
+
+ SimpleLoadBalancer simpleLoadBalancer = new SimpleLoadBalancer();
+
+ simpleLoadBalancer.addServers(servers);
+
+ List allServers = simpleLoadBalancer.getAllServers();
+ List reachableServers = simpleLoadBalancer.getReachableServers();
+ List availableServers = simpleLoadBalancer.getServerList(true);
+
+ Assert.assertEquals(servers.size(), allServers.size());
+ Assert.assertEquals(servers.size(), reachableServers.size());
+ Assert.assertEquals(servers.size(), availableServers.size());
+ }
+
+ @Test
+ public void testSetNull() {
+ SimpleLoadBalancer simpleLoadBalancer = new SimpleLoadBalancer();
+
+ simpleLoadBalancer.addServers(null);
+
+ List allServers = simpleLoadBalancer.getAllServers();
+ List reachableServers = simpleLoadBalancer.getReachableServers();
+ List availableServers = simpleLoadBalancer.getServerList(true);
+
+ Assert.assertEquals(0, allServers.size());
+ Assert.assertEquals(0, reachableServers.size());
+ Assert.assertEquals(0, availableServers.size());
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtilsTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtilsTest.java
new file mode 100644
index 000000000..6078be75c
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtilsTest.java
@@ -0,0 +1,140 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router.feign;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.collect.Sets;
+import feign.Request;
+import feign.RequestTemplate;
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * Test for {@link FeignExpressionLabelUtils}
+ *@author lepdou 2022-05-26
+ */
+public class FeignExpressionLabelUtilsTest {
+
+ @Test
+ public void testGetHeaderLabel() {
+ String headerKey = "uid";
+ String headerValue = "1000";
+ String headerKey2 = "teacher.age";
+ String headerValue2 = "1000";
+
+ RequestTemplate requestTemplate = new RequestTemplate();
+ requestTemplate.header(headerKey, headerValue);
+ requestTemplate.header(headerKey2, headerValue2);
+
+ String labelKey1 = "${http.header.uid}";
+ String labelKey2 = "${http.header.name}";
+ String labelKey3 = "${http.headername}";
+ String labelKey4 = "${http.header.}";
+ String labelKey5 = "${http.header.teacher.age}";
+ Map result = FeignExpressionLabelUtils.resolve(requestTemplate,
+ Sets.newHashSet(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5));
+
+ Assert.assertFalse(result.isEmpty());
+ Assert.assertEquals(headerValue, result.get(labelKey1));
+ Assert.assertEquals(headerValue2, result.get(labelKey5));
+ Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey2)));
+ Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey3)));
+ Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey4)));
+ }
+
+ @Test
+ public void testGetQueryLabel() {
+ String headerKey = "uid";
+ String headerValue = "1000";
+ String headerKey2 = "teacher.age";
+ String headerValue2 = "1000";
+
+ RequestTemplate requestTemplate = new RequestTemplate();
+ requestTemplate.query(headerKey, headerValue);
+ requestTemplate.query(headerKey2, headerValue2);
+
+ String labelKey1 = "${http.query.uid}";
+ String labelKey2 = "${http.query.name}";
+ String labelKey3 = "${http.queryname}";
+ String labelKey4 = "${http.query.}";
+ String labelKey5 = "${http.query.teacher.age}";
+ Map result = FeignExpressionLabelUtils.resolve(requestTemplate,
+ Sets.newHashSet(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5));
+
+ Assert.assertFalse(result.isEmpty());
+ Assert.assertEquals(headerValue, result.get(labelKey1));
+ Assert.assertEquals(headerValue2, result.get(labelKey5));
+ Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey2)));
+ Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey3)));
+ Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey4)));
+ }
+
+ @Test
+ public void testGetMethod() {
+ RequestTemplate requestTemplate = new RequestTemplate();
+ requestTemplate.method(Request.HttpMethod.GET);
+
+ String labelKey1 = "${http.method}";
+ Map result = FeignExpressionLabelUtils.resolve(requestTemplate,
+ Sets.newHashSet(labelKey1));
+
+ Assert.assertFalse(result.isEmpty());
+ Assert.assertEquals("GET", result.get(labelKey1));
+ }
+
+ @Test
+ public void testGetUri() {
+ String uri = "/user/get";
+
+ RequestTemplate requestTemplate = new RequestTemplate();
+ requestTemplate.uri(uri);
+ requestTemplate.method(Request.HttpMethod.GET);
+ requestTemplate.target("http://localhost");
+ requestTemplate = requestTemplate.resolve(new HashMap<>());
+
+ String labelKey1 = "${http.uri}";
+ Map result = FeignExpressionLabelUtils.resolve(requestTemplate,
+ Sets.newHashSet(labelKey1));
+
+ Assert.assertFalse(result.isEmpty());
+ Assert.assertEquals(uri, result.get(labelKey1));
+ }
+
+ @Test
+ public void testGetUri2() {
+ String uri = "/";
+
+ RequestTemplate requestTemplate = new RequestTemplate();
+ requestTemplate.uri(uri);
+ requestTemplate.method(Request.HttpMethod.GET);
+ requestTemplate.target("http://localhost");
+ requestTemplate = requestTemplate.resolve(new HashMap<>());
+
+ String labelKey1 = "${http.uri}";
+ Map result = FeignExpressionLabelUtils.resolve(requestTemplate,
+ Sets.newHashSet(labelKey1));
+
+ Assert.assertFalse(result.isEmpty());
+ Assert.assertEquals(uri, result.get(labelKey1));
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactoryTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactoryTest.java
new file mode 100644
index 000000000..c259e3b60
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactoryTest.java
@@ -0,0 +1,104 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router.feign;
+
+import com.netflix.client.config.DefaultClientConfigImpl;
+import com.netflix.loadbalancer.ILoadBalancer;
+import com.tencent.cloud.polaris.router.SimpleLoadBalancer;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
+import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
+import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for {@link PolarisCachingSpringLoadBalanceFactory}
+ *@author lepdou 2022-05-26
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class PolarisCachingSpringLoadBalanceFactoryTest {
+
+ @Mock
+ private SpringClientFactory factory;
+
+ private String service1 = "service1";
+ private String service2 = "service2";
+
+ @Test
+ public void test() {
+ PolarisCachingSpringLoadBalanceFactory polarisCachingSpringLoadBalanceFactory =
+ new PolarisCachingSpringLoadBalanceFactory(factory, null);
+
+ DefaultClientConfigImpl config1 = new DefaultClientConfigImpl();
+ config1.loadDefaultValues();
+ config1.setClientName(service1);
+ DefaultClientConfigImpl config2 = new DefaultClientConfigImpl();
+ config2.loadDefaultValues();
+ config2.setClientName(service2);
+
+ when(factory.getClientConfig(service1)).thenReturn(config1);
+ when(factory.getClientConfig(service2)).thenReturn(config2);
+
+ ILoadBalancer loadBalancer = new SimpleLoadBalancer();
+ when(factory.getLoadBalancer(service1)).thenReturn(loadBalancer);
+ when(factory.getLoadBalancer(service2)).thenReturn(loadBalancer);
+
+ ServerIntrospector serverIntrospector = new DefaultServerIntrospector();
+ when(factory.getInstance(service1, ServerIntrospector.class)).thenReturn(serverIntrospector);
+ when(factory.getInstance(service2, ServerIntrospector.class)).thenReturn(serverIntrospector);
+
+ // load balancer for service1
+ FeignLoadBalancer feignLoadBalancer = polarisCachingSpringLoadBalanceFactory.create(service1);
+
+ Assert.assertNotNull(feignLoadBalancer);
+ verify(factory).getClientConfig(service1);
+ verify(factory, times(0)).getClientConfig(service2);
+ verify(factory).getLoadBalancer(service1);
+ verify(factory, times(0)).getLoadBalancer(service2);
+ verify(factory).getInstance(service1, ServerIntrospector.class);
+ verify(factory, times(0)).getInstance(service2, ServerIntrospector.class);
+ Assert.assertEquals(loadBalancer, feignLoadBalancer.getLoadBalancer());
+ Assert.assertEquals(service1, feignLoadBalancer.getClientName());
+
+ // load balancer for service2
+ FeignLoadBalancer feignLoadBalancer2 = polarisCachingSpringLoadBalanceFactory.create(service2);
+ // load balancer for service1 again
+ feignLoadBalancer = polarisCachingSpringLoadBalanceFactory.create(service1);
+
+ Assert.assertNotNull(feignLoadBalancer);
+ verify(factory).getClientConfig(service1);
+ verify(factory).getClientConfig(service2);
+ verify(factory).getLoadBalancer(service1);
+ verify(factory).getLoadBalancer(service2);
+ verify(factory).getInstance(service1, ServerIntrospector.class);
+ verify(factory).getInstance(service2, ServerIntrospector.class);
+ Assert.assertEquals(loadBalancer, feignLoadBalancer2.getLoadBalancer());
+ Assert.assertEquals(service2, feignLoadBalancer2.getClientName());
+
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancerTest.java
new file mode 100644
index 000000000..52be650b6
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancerTest.java
@@ -0,0 +1,121 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router.feign;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.netflix.client.config.DefaultClientConfigImpl;
+import com.netflix.loadbalancer.ILoadBalancer;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+import com.tencent.cloud.polaris.router.RouterConstants;
+import com.tencent.cloud.polaris.router.SimpleLoadBalancer;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
+import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
+
+import static org.mockito.Mockito.anyString;
+
+/**
+ * test for {@link PolarisFeignLoadBalancer}
+ * @author lepdou 2022-05-26
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class PolarisFeignLoadBalancerTest {
+
+ @Test
+ public void testHasRouterContext() {
+ DefaultClientConfigImpl config = new DefaultClientConfigImpl();
+ config.loadDefaultValues();
+ ILoadBalancer loadBalancer = new SimpleLoadBalancer();
+ ServerIntrospector serverIntrospector = new DefaultServerIntrospector();
+
+ PolarisFeignLoadBalancer polarisFeignLoadBalancer = new PolarisFeignLoadBalancer(loadBalancer, config, serverIntrospector);
+
+ Map labels = new HashMap<>();
+ labels.put("k1", "v1");
+ labels.put("k2", "v2");
+
+ List headerValues = new ArrayList<>();
+ headerValues.add(JacksonUtils.serialize2Json(labels));
+
+ Map> headers = new HashMap<>();
+ headers.put(RouterConstants.ROUTER_LABEL_HEADER, headerValues);
+
+ // mock ApplicationContextAwareUtils#getProperties
+ try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())).thenReturn("unit-test");
+
+ MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
+ // mock MetadataContextHolder#get
+ try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
+ mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+
+ PolarisRouterContext routerContext = polarisFeignLoadBalancer.buildRouterContext(headers);
+
+ Assert.assertNotNull(routerContext);
+ Map routerLabels = routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS);
+ Assert.assertNotNull(routerLabels);
+ Assert.assertEquals("v1", routerLabels.get("k1"));
+ Assert.assertEquals("v2", routerLabels.get("k2"));
+ Assert.assertNull(routerLabels.get("k3"));
+ }
+ }
+ }
+
+ @Test
+ public void testHasNoneRouterContext() {
+ DefaultClientConfigImpl config = new DefaultClientConfigImpl();
+ config.loadDefaultValues();
+ ILoadBalancer loadBalancer = new SimpleLoadBalancer();
+ ServerIntrospector serverIntrospector = new DefaultServerIntrospector();
+
+ PolarisFeignLoadBalancer polarisFeignLoadBalancer = new PolarisFeignLoadBalancer(loadBalancer, config, serverIntrospector);
+
+ Map> headers = new HashMap<>();
+
+ // mock ApplicationContextAwareUtils#getProperties
+ try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())).thenReturn("unit-test");
+
+ MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
+ // mock MetadataContextHolder#get
+ try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
+ mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+
+ PolarisRouterContext routerContext = polarisFeignLoadBalancer.buildRouterContext(headers);
+
+ Assert.assertNull(routerContext);
+ }
+ }
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java
new file mode 100644
index 000000000..d5f7076fc
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java
@@ -0,0 +1,142 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 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.router.feign;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.cloud.common.util.ExpressionLabelUtils;
+import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.polaris.router.RouterConstants;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
+import feign.RequestTemplate;
+import feign.Target;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+/**
+ * test for {@link RouterLabelFeignInterceptor}
+ * @author lepdou 2022-05-26
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class RouterLabelFeignInterceptorTest {
+
+ @Mock
+ private MetadataLocalProperties metadataLocalProperties;
+ @Mock
+ private RouterRuleLabelResolver routerRuleLabelResolver;
+ @Mock
+ private RouterLabelResolver routerLabelResolver;
+
+ @Test
+ public void testResolveRouterLabel() {
+ RouterLabelFeignInterceptor routerLabelFeignInterceptor = new RouterLabelFeignInterceptor(
+ Collections.singletonList(routerLabelResolver),
+ metadataLocalProperties, routerRuleLabelResolver);
+
+ // mock request template
+ RequestTemplate requestTemplate = new RequestTemplate();
+ String headerUidKey = "uid";
+ String headerUidValue = "1000";
+ requestTemplate.header(headerUidKey, headerUidValue);
+ String peerService = "peerService";
+ Target.EmptyTarget