fix count circuit breaker in gateway & return 404 when context api does not match (#1508)
parent
4dbf8125d6
commit
dd39bf7648
@ -0,0 +1,31 @@
|
||||
spring:
|
||||
cloud:
|
||||
tencent:
|
||||
gateway:
|
||||
groups:
|
||||
group-scg2p:
|
||||
predicate:
|
||||
apiType: ms
|
||||
context: /group1
|
||||
namespace:
|
||||
key: null
|
||||
position: PATH
|
||||
service:
|
||||
key: null
|
||||
position: PATH
|
||||
routes:
|
||||
- host: null
|
||||
metadata: {}
|
||||
method: GET
|
||||
namespace: default
|
||||
path: /echo/{param}
|
||||
service: provider-demo
|
||||
routes:
|
||||
group1:
|
||||
filters:
|
||||
- Context=group1
|
||||
order: -1
|
||||
predicates:
|
||||
- Context=group1
|
||||
- Path=/group1/**
|
||||
uri: lb://group1
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.cloud.tencent.gateway.routes",
|
||||
"type": "java.util.Map<java.lang.String, org.springframework.cloud.gateway.route.RouteDefinition>",
|
||||
"description": "Route definitions map for gateway context",
|
||||
"sourceType": "com.tencent.cloud.plugin.gateway.context.ContextGatewayProperties",
|
||||
"defaultValue": {}
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.tencent.gateway.groups",
|
||||
"type": "java.util.Map<java.lang.String, com.tencent.cloud.plugin.gateway.context.GroupContext>",
|
||||
"description": "Group contexts configuration",
|
||||
"sourceType": "com.tencent.cloud.plugin.gateway.context.ContextGatewayProperties",
|
||||
"defaultValue": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.gateway;
|
||||
|
||||
import com.tencent.cloud.plugin.gateway.context.ContextGatewayFilterFactory;
|
||||
import com.tencent.cloud.plugin.gateway.context.ContextGatewayProperties;
|
||||
import com.tencent.cloud.plugin.gateway.context.ContextGatewayPropertiesManager;
|
||||
import com.tencent.cloud.plugin.gateway.context.ContextPropertiesRouteDefinitionLocator;
|
||||
import com.tencent.cloud.plugin.gateway.context.ContextRoutePredicateFactory;
|
||||
import com.tencent.cloud.plugin.gateway.context.GatewayConfigChangeListener;
|
||||
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
|
||||
import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent;
|
||||
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
|
||||
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClient;
|
||||
import com.tencent.cloud.polaris.discovery.reactive.PolarisReactiveDiscoveryClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link GatewayPluginAutoConfiguration}.
|
||||
*/
|
||||
class GatewayPluginAutoConfigurationTest {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
ConfigurationPropertiesAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
PolarisContextAutoConfiguration.class,
|
||||
GatewayAutoConfiguration.class,
|
||||
GatewayPluginAutoConfiguration.class
|
||||
))
|
||||
.withPropertyValues(
|
||||
"spring.cloud.gateway.enabled=false", // not needed for this test
|
||||
"spring.cloud.tencent.plugin.scg.enabled=true",
|
||||
"spring.cloud.tencent.plugin.scg.context.enabled=true",
|
||||
"spring.cloud.tencent.gateway.routes.test_group.uri=lb://test-group"
|
||||
)
|
||||
.withUserConfiguration(MockPolarisClientsConfiguration.class);
|
||||
|
||||
@Test
|
||||
void shouldCreateBeansWhenConditionsMet() {
|
||||
contextRunner.run(context -> {
|
||||
// Verify core beans
|
||||
assertThat(context).hasSingleBean(ContextGatewayFilterFactory.class);
|
||||
assertThat(context).hasSingleBean(ContextPropertiesRouteDefinitionLocator.class);
|
||||
assertThat(context).hasSingleBean(ContextRoutePredicateFactory.class);
|
||||
assertThat(context).hasSingleBean(ContextGatewayPropertiesManager.class);
|
||||
assertThat(context).hasSingleBean(GatewayRegistrationCustomizer.class);
|
||||
assertThat(context).hasSingleBean(GatewayConfigChangeListener.class);
|
||||
assertThat(context).hasSingleBean(PolarisReactiveLoadBalancerClientFilterBeanPostProcessor.class);
|
||||
|
||||
|
||||
GatewayPluginAutoConfiguration.ContextPluginConfiguration pluginConfiguration =
|
||||
context.getBean(GatewayPluginAutoConfiguration.ContextPluginConfiguration.class);
|
||||
assertThat(pluginConfiguration).hasFieldOrPropertyWithValue("commonEagerLoadEnabled", true)
|
||||
.hasFieldOrPropertyWithValue("gatewayEagerLoadEnabled", false);
|
||||
|
||||
ContextGatewayProperties properties = context.getBean(ContextGatewayProperties.class);
|
||||
properties.setRoutes(properties.getRoutes());
|
||||
properties.setGroups(properties.getGroups());
|
||||
properties.toString();
|
||||
|
||||
// test listener
|
||||
GatewayConfigChangeListener listener = context.getBean(GatewayConfigChangeListener.class);
|
||||
listener.onChangeTencentGatewayProperties(new ConfigChangeEvent(null, null));
|
||||
listener.onChangeGatewayConfigChangeListener(new ConfigChangeEvent(null, null));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEagerLoad() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.cloud.polaris.discovery.eager-load.gateway.enabled=true")
|
||||
.run(context -> {
|
||||
// Verify eager loading
|
||||
GatewayPluginAutoConfiguration.ContextPluginConfiguration pluginConfiguration = context.getBean(GatewayPluginAutoConfiguration.ContextPluginConfiguration.class);
|
||||
assertThat(pluginConfiguration).hasFieldOrPropertyWithValue("commonEagerLoadEnabled", true)
|
||||
.hasFieldOrPropertyWithValue("gatewayEagerLoadEnabled", true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotCreateBeansWhenPluginDisabled() {
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(GatewayPluginAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.tencent.plugin.scg.enabled=false")
|
||||
.run(context -> assertThat(context).doesNotHaveBean(ContextGatewayFilterFactory.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotCreateContextBeansWhenContextPluginDisabled() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.cloud.tencent.plugin.scg.context.enabled=false")
|
||||
.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ContextGatewayFilterFactory.class);
|
||||
assertThat(context).doesNotHaveBean(ContextPropertiesRouteDefinitionLocator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class MockPolarisClientsConfiguration {
|
||||
@Bean
|
||||
PolarisDiscoveryClient polarisDiscoveryClient() {
|
||||
return mock(PolarisDiscoveryClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PolarisReactiveDiscoveryClient polarisReactiveDiscoveryClient() {
|
||||
return mock(PolarisReactiveDiscoveryClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PolarisConfigProperties polarisConfigProperties() {
|
||||
return new PolarisConfigProperties();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.gateway;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tencent.cloud.polaris.registry.PolarisRegistration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class GatewayRegistrationCustomizerTest {
|
||||
|
||||
@Mock
|
||||
private PolarisRegistration polarisRegistration;
|
||||
|
||||
private final GatewayRegistrationCustomizer customizer = new GatewayRegistrationCustomizer();
|
||||
|
||||
@Test
|
||||
void shouldAddServiceTypeMetadata() {
|
||||
// Arrange
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
when(polarisRegistration.getMetadata()).thenReturn(metadata);
|
||||
|
||||
// Act
|
||||
customizer.customize(polarisRegistration);
|
||||
|
||||
// Assert
|
||||
assertThat(metadata)
|
||||
.containsEntry("internal-service-type", "spring-cloud-gateway");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotOverrideExistingMetadata() {
|
||||
// Arrange
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put("existing-key", "existing-value");
|
||||
metadata.put("internal-service-type", "existing-type");
|
||||
when(polarisRegistration.getMetadata()).thenReturn(metadata);
|
||||
|
||||
// Act
|
||||
customizer.customize(polarisRegistration);
|
||||
|
||||
// Assert
|
||||
assertThat(metadata)
|
||||
.containsEntry("internal-service-type", "spring-cloud-gateway")
|
||||
.containsEntry("existing-key", "existing-value");
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.gateway;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
|
||||
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
* Test for ${@link PolarisReactiveLoadBalancerClientFilterBeanPostProcessor}
|
||||
*
|
||||
* @author Shedfree Wu
|
||||
*/
|
||||
public class PolarisReactiveLoadBalancerClientFilterBeanPostProcessorTest {
|
||||
|
||||
@Mock
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Mock
|
||||
private LoadBalancerClientFactory loadBalancerClientFactory;
|
||||
|
||||
@Mock
|
||||
private GatewayLoadBalancerProperties gatewayLoadBalancerProperties;
|
||||
|
||||
private PolarisReactiveLoadBalancerClientFilterBeanPostProcessor processor;
|
||||
|
||||
@BeforeEach
|
||||
protected void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
processor = new PolarisReactiveLoadBalancerClientFilterBeanPostProcessor(applicationContext);
|
||||
|
||||
when(applicationContext.getBean(GatewayLoadBalancerProperties.class)).thenReturn(gatewayLoadBalancerProperties);
|
||||
when(applicationContext.getBean(LoadBalancerClientFactory.class)).thenReturn(loadBalancerClientFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOrder() {
|
||||
int order = processor.getOrder();
|
||||
Assertions.assertEquals(PolarisReactiveLoadBalancerClientFilterBeanPostProcessor.ORDER, order);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPostProcessBeforeInitializationWithReactiveLoadBalancerClientFilter() {
|
||||
// Arrange
|
||||
ReactiveLoadBalancerClientFilter originalInterceptor = mock(ReactiveLoadBalancerClientFilter.class);
|
||||
String beanName = "testBean";
|
||||
|
||||
// Act
|
||||
Object result = processor.postProcessAfterInitialization(originalInterceptor, beanName);
|
||||
|
||||
// Assert
|
||||
Assertions.assertInstanceOf(PolarisReactiveLoadBalancerClientFilter.class, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPostProcessBeforeInitializationWithNonReactiveLoadBalancerClientFilter() {
|
||||
// Arrange
|
||||
Object originalBean = new Object();
|
||||
String beanName = "testBean";
|
||||
|
||||
// Act
|
||||
Object result = processor.postProcessAfterInitialization(originalBean, beanName);
|
||||
|
||||
// Assert
|
||||
Assertions.assertSame(originalBean, result);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.gateway;
|
||||
|
||||
import com.tencent.cloud.common.constant.MetadataConstant;
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link PolarisReactiveLoadBalancerClientFilter}.
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PolarisReactiveLoadBalancerClientFilterTest {
|
||||
|
||||
@Mock
|
||||
private LoadBalancerClientFactory clientFactory;
|
||||
@Mock
|
||||
private GatewayLoadBalancerProperties properties;
|
||||
@Mock
|
||||
private ReactiveLoadBalancerClientFilter originalFilter;
|
||||
@Mock
|
||||
private ServerWebExchange exchange;
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
private PolarisReactiveLoadBalancerClientFilter polarisFilter;
|
||||
private final MetadataContext testContext = new MetadataContext();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
polarisFilter = new PolarisReactiveLoadBalancerClientFilter(clientFactory, properties, originalFilter);
|
||||
MetadataContextHolder.remove();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOrderDelegatesToOriginalFilter() {
|
||||
// Arrange
|
||||
when(originalFilter.getOrder()).thenReturn(42);
|
||||
|
||||
// Act
|
||||
assertThat(polarisFilter.getOrder()).isEqualTo(42);
|
||||
verify(originalFilter).getOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilterRestoresMetadataContext() {
|
||||
// Arrange
|
||||
when(exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT))
|
||||
.thenReturn(testContext);
|
||||
when(originalFilter.filter(exchange, chain))
|
||||
.thenReturn(Mono.empty());
|
||||
MetadataContext before = MetadataContextHolder.get();
|
||||
Assertions.assertNotEquals(testContext, before);
|
||||
// Act
|
||||
polarisFilter.filter(exchange, chain);
|
||||
MetadataContext after = MetadataContextHolder.get();
|
||||
Assertions.assertEquals(testContext, after);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilterWithoutMetadataContext() {
|
||||
// Arrange
|
||||
when(exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT))
|
||||
.thenReturn(null);
|
||||
when(originalFilter.filter(exchange, chain))
|
||||
.thenReturn(Mono.empty());
|
||||
MetadataContext before = MetadataContextHolder.get();
|
||||
// Act
|
||||
polarisFilter.filter(exchange, chain);
|
||||
MetadataContext after = MetadataContextHolder.get();
|
||||
|
||||
// Assert
|
||||
Assertions.assertEquals(before, after);
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.gateway.context;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContextGatewayFilterFactory}.
|
||||
*/
|
||||
class ContextGatewayFilterFactoryTest {
|
||||
|
||||
@Mock
|
||||
private ContextGatewayPropertiesManager mockManager;
|
||||
|
||||
private ContextGatewayFilterFactory filterFactory;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
filterFactory = new ContextGatewayFilterFactory(mockManager);
|
||||
}
|
||||
|
||||
// Test filter factory creates filter with correct configuration
|
||||
@Test
|
||||
void shouldCreateFilterWithValidConfig() {
|
||||
// Setup test config
|
||||
ContextGatewayFilterFactory.Config config = new ContextGatewayFilterFactory.Config();
|
||||
config.setGroup("test-group");
|
||||
|
||||
// Create filter instance
|
||||
GatewayFilter filter = filterFactory.apply(config);
|
||||
|
||||
// Verify filter creation
|
||||
assertThat(filter)
|
||||
.isInstanceOf(ContextGatewayFilter.class)
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
// Test shortcut field order configuration
|
||||
@Test
|
||||
void shouldReturnCorrectShortcutFieldOrder() {
|
||||
// Execute & Verify
|
||||
assertThat(filterFactory.shortcutFieldOrder())
|
||||
.containsExactly("group");
|
||||
}
|
||||
|
||||
// Test config class getter/setter behavior
|
||||
@Test
|
||||
void shouldHandleConfigGroupPropertyCorrectly() {
|
||||
// Setup config
|
||||
ContextGatewayFilterFactory.Config config = new ContextGatewayFilterFactory.Config();
|
||||
final String expectedGroup = "payment-service";
|
||||
|
||||
// Test setter/getter
|
||||
config.setGroup(expectedGroup);
|
||||
assertThat(config.getGroup())
|
||||
.isEqualTo(expectedGroup);
|
||||
}
|
||||
|
||||
|
||||
// Test filter creation with empty group name
|
||||
@Test
|
||||
void shouldHandleEmptyGroupNameInConfig() {
|
||||
// Setup config with empty group
|
||||
ContextGatewayFilterFactory.Config config = new ContextGatewayFilterFactory.Config();
|
||||
config.setGroup("");
|
||||
|
||||
// Create filter instance
|
||||
GatewayFilter filter = filterFactory.apply(config);
|
||||
|
||||
// Verify filter creation
|
||||
assertThat(filter)
|
||||
.isNotNull()
|
||||
.isInstanceOf(ContextGatewayFilter.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.gateway.context;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.tencent.cloud.common.constant.MetadataConstant;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
/**
|
||||
* Test class for {@link ContextGatewayFilter}.
|
||||
*/
|
||||
class ContextGatewayFilterTest {
|
||||
|
||||
@Mock
|
||||
private ContextGatewayPropertiesManager mockManager;
|
||||
@Mock
|
||||
private GatewayFilterChain mockChain;
|
||||
|
||||
private ContextGatewayFilter filter;
|
||||
private GroupContext groupContext;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
ContextGatewayFilterFactory.Config config = new ContextGatewayFilterFactory.Config();
|
||||
config.setGroup("testGroup");
|
||||
filter = new ContextGatewayFilter(mockManager, config);
|
||||
groupContext = new GroupContext();
|
||||
when(mockManager.getGroups()).thenReturn(Collections.singletonMap("testGroup", groupContext));
|
||||
}
|
||||
|
||||
// Test EXTERNAL API path reconstruction
|
||||
@Test
|
||||
void shouldHandleExternalApiPathCorrectly() {
|
||||
// Setup group context
|
||||
GroupContext.ContextPredicate predicate = createPredicate(ApiType.EXTERNAL, Position.PATH, Position.PATH);
|
||||
groupContext.setPredicate(predicate);
|
||||
groupContext.setRoutes(Collections.singletonList(
|
||||
createContextRoute("GET|/external/api", "GET", "testNS", "testSvc")
|
||||
));
|
||||
|
||||
// Create test request
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/context/external/api").build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
GroupContext.ContextRoute route = createContextRoute("GET|/external/api", "GET", "http://test.com");
|
||||
|
||||
when(mockManager.getGroupPathRoute("testGroup", "GET|/external/api")).thenReturn(route);
|
||||
|
||||
// Execute filter
|
||||
Mono<Void> result = filter.filter(exchange, mockChain);
|
||||
|
||||
// Verify final URL
|
||||
URI finalUri = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(finalUri.toString()).isEqualTo("http://test.com/external/api");
|
||||
}
|
||||
|
||||
// Test metadata update functionality
|
||||
@Test
|
||||
void shouldUpdateRouteMetadataCorrectly() throws Exception {
|
||||
// Setup context route with metadata
|
||||
GroupContext.ContextRoute route = createContextRoute("GET|/api", "GET", "ns", "svc");
|
||||
route.setMetadata(Collections.singletonMap("version", "v2"));
|
||||
|
||||
// Setup group context
|
||||
GroupContext.ContextPredicate predicate = createPredicate(ApiType.MS, Position.PATH, Position.PATH);
|
||||
groupContext.setPredicate(predicate);
|
||||
groupContext.setRoutes(Collections.singletonList(route));
|
||||
|
||||
// Create test request
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/context/ns/svc/api").build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, Route.async().id("test").uri(URI.create("lb://svc")).
|
||||
predicate((GatewayPredicate) serverWebExchange -> false).build());
|
||||
when(mockManager.getGroupPathRoute("testGroup", "GET|/ns/svc/api")).thenReturn(route);
|
||||
|
||||
exchange.getAttributes().put(MetadataConstant.HeaderName.METADATA_CONTEXT, MetadataContextHolder.get());
|
||||
// Execute filter
|
||||
filter.filter(exchange, mockChain);
|
||||
|
||||
// Route updatedRoute = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
|
||||
// assertThat(updatedRoute.getMetadata()).containsEntry("version", "v2");
|
||||
// no context in exchange
|
||||
exchange.getAttributes().remove(MetadataConstant.HeaderName.METADATA_CONTEXT);
|
||||
filter.filter(exchange, mockChain);
|
||||
|
||||
}
|
||||
|
||||
// Helper method to create test predicate
|
||||
private GroupContext.ContextPredicate createPredicate(ApiType apiType, Position nsPos, Position svcPos) {
|
||||
GroupContext.ContextPredicate predicate = new GroupContext.ContextPredicate();
|
||||
predicate.setApiType(apiType);
|
||||
|
||||
GroupContext.ContextNamespace namespace = new GroupContext.ContextNamespace();
|
||||
namespace.setPosition(nsPos);
|
||||
namespace.setKey("ns-key");
|
||||
predicate.setNamespace(namespace);
|
||||
|
||||
GroupContext.ContextService service = new GroupContext.ContextService();
|
||||
service.setPosition(svcPos);
|
||||
service.setKey("svc-key");
|
||||
predicate.setService(service);
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
// Helper method to create context route
|
||||
private GroupContext.ContextRoute createContextRoute(String path, String method, String ns, String svc) {
|
||||
GroupContext.ContextRoute route = new GroupContext.ContextRoute();
|
||||
route.setPath(path);
|
||||
route.setMethod(method);
|
||||
route.setNamespace(ns);
|
||||
route.setService(svc);
|
||||
return route;
|
||||
}
|
||||
|
||||
private GroupContext.ContextRoute createContextRoute(String path, String method, String host) {
|
||||
GroupContext.ContextRoute route = new GroupContext.ContextRoute();
|
||||
route.setPath(path);
|
||||
route.setMethod(method);
|
||||
route.setHost(host);
|
||||
return route;
|
||||
}
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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.gateway.context;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClient;
|
||||
import com.tencent.cloud.polaris.discovery.reactive.PolarisReactiveDiscoveryClient;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test for {@link ContextGatewayPropertiesManager}.
|
||||
*/
|
||||
class ContextGatewayPropertiesManagerTest {
|
||||
|
||||
private ContextGatewayPropertiesManager manager;
|
||||
private PolarisDiscoveryClient mockDiscoveryClient;
|
||||
private PolarisReactiveDiscoveryClient mockReactiveClient;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
manager = new ContextGatewayPropertiesManager();
|
||||
mockDiscoveryClient = Mockito.mock(PolarisDiscoveryClient.class);
|
||||
mockReactiveClient = Mockito.mock(PolarisReactiveDiscoveryClient.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleEmptyGroupsWhenSettingRouteMap() {
|
||||
// Test empty groups handling
|
||||
manager.setGroupRouteMap(null);
|
||||
assertThat(manager.getGroupPathRouteMap()).isEmpty();
|
||||
assertThat(manager.getGroups()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldClassifyRoutesByPathType() {
|
||||
// Prepare test data
|
||||
Map<String, GroupContext> groups = new HashMap<>();
|
||||
GroupContext group1 = new GroupContext();
|
||||
GroupContext.ContextRoute normalRoute = new GroupContext.ContextRoute();
|
||||
normalRoute.setPath("/api/v1/normal");
|
||||
normalRoute.setMethod("POST");
|
||||
normalRoute.setNamespace("testNS");
|
||||
normalRoute.setService("testSvc");
|
||||
GroupContext.ContextRoute wildcardRoute = new GroupContext.ContextRoute();
|
||||
wildcardRoute.setPath("/api/wildcard/**");
|
||||
wildcardRoute.setMethod("GET");
|
||||
wildcardRoute.setNamespace("testNS");
|
||||
wildcardRoute.setService("testSvc");
|
||||
group1.setRoutes(Arrays.asList(normalRoute, wildcardRoute));
|
||||
GroupContext.ContextPredicate predicate = new GroupContext.ContextPredicate();
|
||||
predicate.setApiType(ApiType.MS);
|
||||
group1.setPredicate(predicate);
|
||||
groups.put("group1", group1);
|
||||
|
||||
// Execute
|
||||
manager.setGroupRouteMap(groups);
|
||||
|
||||
// Verify classification
|
||||
Map<String, Map<String, GroupContext.ContextRoute>> pathMap = manager.getGroupPathRouteMap();
|
||||
Map<String, Map<String, GroupContext.ContextRoute>> wildcardMap = manager.getGroupWildcardPathRouteMap();
|
||||
assertThat(pathMap.get("group1")).hasSize(1);
|
||||
assertThat(wildcardMap.get("group1")).hasSize(1);
|
||||
|
||||
// Execute eager load
|
||||
manager.eagerLoad(mockDiscoveryClient, null);
|
||||
|
||||
when(mockReactiveClient.getInstances("testSvc")).thenReturn(Flux.fromIterable(Collections.emptyList()));
|
||||
manager.eagerLoad(null, mockReactiveClient);
|
||||
|
||||
manager.eagerLoad(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchExactPathBeforeWildcard() {
|
||||
// Setup test routes
|
||||
GroupContext group = new GroupContext();
|
||||
GroupContext.ContextRoute exactRoute = new GroupContext.ContextRoute();
|
||||
exactRoute.setPath("/api/exact");
|
||||
exactRoute.setMethod("POST");
|
||||
exactRoute.setNamespace("testNS");
|
||||
exactRoute.setService("testSvc");
|
||||
GroupContext.ContextRoute wildcardRoute = new GroupContext.ContextRoute();
|
||||
wildcardRoute.setPath("/api/*/wildcard");
|
||||
wildcardRoute.setMethod("GET");
|
||||
wildcardRoute.setNamespace("testNS");
|
||||
wildcardRoute.setService("testSvc");
|
||||
|
||||
group.setRoutes(Arrays.asList(exactRoute, wildcardRoute));
|
||||
GroupContext.ContextPredicate predicate = new GroupContext.ContextPredicate();
|
||||
predicate.setApiType(ApiType.MS);
|
||||
GroupContext.ContextNamespace namespace = new GroupContext.ContextNamespace();
|
||||
namespace.setPosition(Position.PATH);
|
||||
predicate.setNamespace(namespace);
|
||||
GroupContext.ContextService service = new GroupContext.ContextService();
|
||||
service.setPosition(Position.PATH);
|
||||
predicate.setService(service);
|
||||
group.setPredicate(predicate);
|
||||
manager.setGroupRouteMap(Collections.singletonMap("testGroup", group));
|
||||
|
||||
|
||||
ContextGatewayFilter filter = new ContextGatewayFilter(manager, null);
|
||||
MockServerHttpRequest request = MockServerHttpRequest.post("http://localhost/context/testNS/testSvc/api/exact").build();
|
||||
String[] apis = filter.rebuildMsApi(request, group, request.getPath().value());
|
||||
|
||||
// Test path matching
|
||||
GroupContext.ContextRoute result = manager.getGroupPathRoute("testGroup", apis[0]);
|
||||
assertThat(result).isEqualTo(exactRoute);
|
||||
// Test wildcard matching
|
||||
request = MockServerHttpRequest.get("http://localhost/context/testNS/testSvc/api/test/wildcard").build();
|
||||
apis = filter.rebuildMsApi(request, group, request.getPath().value());
|
||||
result = manager.getGroupPathRoute("testGroup", apis[0]);
|
||||
assertThat(result).isEqualTo(wildcardRoute);
|
||||
// Test non-matching
|
||||
request = MockServerHttpRequest.get("http://localhost/context/testNS/testSvc/api/wildcard").build();
|
||||
apis = filter.rebuildMsApi(request, group, request.getPath().value());
|
||||
result = manager.getGroupPathRoute("testGroup", apis[0]);
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
// Helper method to create test context route
|
||||
private GroupContext.ContextRoute createContextRoute(String path, String method, String namespace, String service) {
|
||||
GroupContext.ContextRoute route = new GroupContext.ContextRoute();
|
||||
route.setPath(path);
|
||||
route.setMethod(method);
|
||||
route.setNamespace(namespace);
|
||||
route.setService(service);
|
||||
return route;
|
||||
}
|
||||
|
||||
// Helper method to create group context with configurable positions
|
||||
private GroupContext createGroupContext(ApiType apiType, Position namespacePos, Position servicePos) {
|
||||
GroupContext group = new GroupContext();
|
||||
GroupContext.ContextPredicate predicate = new GroupContext.ContextPredicate();
|
||||
predicate.setApiType(apiType);
|
||||
|
||||
GroupContext.ContextNamespace namespace = new GroupContext.ContextNamespace();
|
||||
namespace.setPosition(namespacePos);
|
||||
namespace.setKey("ns-key");
|
||||
predicate.setNamespace(namespace);
|
||||
|
||||
GroupContext.ContextService service = new GroupContext.ContextService();
|
||||
service.setPosition(servicePos);
|
||||
service.setKey("svc-key");
|
||||
predicate.setService(service);
|
||||
|
||||
group.setPredicate(predicate);
|
||||
return group;
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMultiplePositionCombinations() {
|
||||
// ns position PATH
|
||||
testPositionCombination(ApiType.MS, Position.PATH, Position.PATH,
|
||||
"/context/nsFromPath/svcFromPath/api/test",
|
||||
"POST|/nsFromPath/svcFromPath/api/test",
|
||||
"/api/test");
|
||||
|
||||
testPositionCombination(ApiType.MS, Position.PATH, Position.HEADER,
|
||||
"/context/nsFromPath/api/test",
|
||||
"POST|/nsFromPath/svcFromHeader/api/test",
|
||||
"/api/test");
|
||||
|
||||
testPositionCombination(ApiType.MS, Position.PATH, Position.QUERY,
|
||||
"/context/nsFromPath/api/test?svc-key=querySVC",
|
||||
"POST|/nsFromPath/querySVC/api/test",
|
||||
"/api/test");
|
||||
// ns position QUERY
|
||||
testPositionCombination(ApiType.MS, Position.QUERY, Position.PATH,
|
||||
"/context/svcFromPath/api/test?ns-key=queryNS",
|
||||
"POST|/queryNS/svcFromPath/api/test",
|
||||
"/api/test");
|
||||
|
||||
testPositionCombination(ApiType.MS, Position.QUERY, Position.QUERY,
|
||||
"/context/api/test?ns-key=queryNS&svc-key=querySVC",
|
||||
"POST|/queryNS/querySVC/api/test",
|
||||
"/api/test");
|
||||
|
||||
testPositionCombination(ApiType.MS, Position.QUERY, Position.HEADER,
|
||||
"/context/api/test?ns-key=queryNS",
|
||||
"POST|/queryNS/svcFromHeader/api/test",
|
||||
"/api/test");
|
||||
// ns position HEADER
|
||||
testPositionCombination(ApiType.MS, Position.HEADER, Position.PATH,
|
||||
"/context/svcFromPath/api/test",
|
||||
"POST|/headerNS/svcFromPath/api/test",
|
||||
"/api/test");
|
||||
|
||||
testPositionCombination(ApiType.MS, Position.HEADER, Position.QUERY,
|
||||
"/context/api/test?svc-key=querySVC",
|
||||
"POST|/headerNS/querySVC/api/test",
|
||||
"/api/test");
|
||||
|
||||
testPositionCombination(ApiType.MS, Position.HEADER, Position.HEADER,
|
||||
"/context/api/test",
|
||||
"POST|/headerNS/svcFromHeader/api/test",
|
||||
"/api/test");
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void testPositionCombination(ApiType apiType, Position namespacePos, Position servicePos,
|
||||
String inputPath, String expectedMatchPath, String expectedRealPath) {
|
||||
// Setup group with specified positions
|
||||
GroupContext group = createGroupContext(apiType, namespacePos, servicePos);
|
||||
group.setRoutes(Collections.singletonList(
|
||||
createContextRoute(expectedMatchPath, "POST", "testNS", "testSvc")
|
||||
));
|
||||
manager.setGroupRouteMap(Collections.singletonMap("testGroup", group));
|
||||
|
||||
// Build test request with appropriate parameters
|
||||
MockServerHttpRequest.BaseBuilder<?> requestBuilder = MockServerHttpRequest.post(inputPath);
|
||||
switch (namespacePos) {
|
||||
case HEADER:
|
||||
requestBuilder.header("ns-key", "headerNS");
|
||||
break;
|
||||
case QUERY:
|
||||
// Query param already in URL
|
||||
break;
|
||||
}
|
||||
switch (servicePos) {
|
||||
case HEADER:
|
||||
requestBuilder.header("svc-key", "svcFromHeader");
|
||||
break;
|
||||
case QUERY:
|
||||
// Query param already in URL
|
||||
break;
|
||||
}
|
||||
|
||||
ContextGatewayFilter filter = new ContextGatewayFilter(manager, null);
|
||||
MockServerHttpRequest mockServerHttpRequest = requestBuilder.build();
|
||||
String[] apis = filter.rebuildMsApi(mockServerHttpRequest, group, mockServerHttpRequest.getPath().value());
|
||||
|
||||
// Verify path reconstruction
|
||||
assertThat(apis[0]).isEqualTo(expectedMatchPath);
|
||||
assertThat(apis[1]).isEqualTo(expectedRealPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleExternalApiType() {
|
||||
// Test EXTERNAL API type
|
||||
GroupContext group = createGroupContext(ApiType.EXTERNAL, Position.PATH, Position.PATH);
|
||||
group.setRoutes(Collections.singletonList(
|
||||
createContextRoute("POST|/external/api", "POST", null, null)
|
||||
));
|
||||
manager.setGroupRouteMap(Collections.singletonMap("externalGroup", group));
|
||||
|
||||
ContextGatewayFilter filter = new ContextGatewayFilter(manager, null);
|
||||
String inputPath = "/context/external/api";
|
||||
String[] apis = filter.rebuildExternalApi(
|
||||
MockServerHttpRequest.post(inputPath).build(),
|
||||
inputPath
|
||||
);
|
||||
|
||||
assertThat(apis[0]).isEqualTo("POST|/external/api");
|
||||
assertThat(apis[1]).isEqualTo("/external/api");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGroupContext() {
|
||||
GroupContext group1 = new GroupContext();
|
||||
group1.setComment("testComment");
|
||||
Assertions.assertEquals("testComment", group1.getComment());
|
||||
|
||||
GroupContext.ContextPredicate contextPredicate = new GroupContext.ContextPredicate();
|
||||
|
||||
contextPredicate.setContext("testContext");
|
||||
Assertions.assertEquals("testContext", contextPredicate.getContext());
|
||||
|
||||
GroupContext.ContextRoute contextRoute = new GroupContext.ContextRoute();
|
||||
contextRoute.setPathMapping("testPathMapping");
|
||||
Assertions.assertEquals("testPathMapping", contextRoute.getPathMapping());
|
||||
contextRoute.setHost("testHost");
|
||||
Assertions.assertEquals("testHost", contextRoute.getHost());
|
||||
contextRoute.setMetadata(Collections.singletonMap("testKey", "testValue"));
|
||||
Assertions.assertEquals(1, contextRoute.getMetadata().size());
|
||||
}
|
||||
}
|
@ -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.plugin.gateway.context;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContextPropertiesRouteDefinitionLocator}.
|
||||
*/
|
||||
class ContextPropertiesRouteDefinitionLocatorTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyWhenNoRoutes() {
|
||||
|
||||
ContextGatewayProperties mockProps = mock(ContextGatewayProperties.class);
|
||||
when(mockProps.getRoutes()).thenReturn(Collections.emptyMap());
|
||||
|
||||
ContextPropertiesRouteDefinitionLocator locator = new ContextPropertiesRouteDefinitionLocator(mockProps);
|
||||
|
||||
Flux<RouteDefinition> result = locator.getRouteDefinitions();
|
||||
StepVerifier.create(result)
|
||||
.expectNextCount(0)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAllRouteDefinitions() {
|
||||
|
||||
Map<String, RouteDefinition> testRoutes = new HashMap<>();
|
||||
testRoutes.put("route1", new RouteDefinition());
|
||||
testRoutes.put("route2", new RouteDefinition());
|
||||
|
||||
ContextGatewayProperties mockProps = mock(ContextGatewayProperties.class);
|
||||
when(mockProps.getRoutes()).thenReturn(testRoutes);
|
||||
|
||||
ContextPropertiesRouteDefinitionLocator locator = new ContextPropertiesRouteDefinitionLocator(mockProps);
|
||||
|
||||
Flux<RouteDefinition> result = locator.getRouteDefinitions();
|
||||
StepVerifier.create(result)
|
||||
.expectNextCount(2)
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.gateway.context;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContextRoutePredicateFactory}.
|
||||
*/
|
||||
class ContextRoutePredicateFactoryTest {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(ContextRoutePredicateFactory.class);
|
||||
|
||||
@Test
|
||||
void shouldCreateConfigWithGroup() {
|
||||
contextRunner.run(context -> {
|
||||
// Arrange
|
||||
ContextRoutePredicateFactory factory = context.getBean(ContextRoutePredicateFactory.class);
|
||||
factory.shortcutFieldOrder();
|
||||
|
||||
// Act
|
||||
ContextRoutePredicateFactory.Config config = factory.newConfig();
|
||||
config.setGroup("g1");
|
||||
Assertions.assertEquals("g1", config.getGroup());
|
||||
|
||||
GatewayPredicate gatewayPredicate = (GatewayPredicate) factory.apply(config);
|
||||
gatewayPredicate.toString();
|
||||
Assertions.assertTrue(gatewayPredicate.test(null));
|
||||
Assertions.assertEquals(config, gatewayPredicate.getConfig());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAlwaysMatchWhenNotImplemented() {
|
||||
contextRunner.run(context -> {
|
||||
// Arrange
|
||||
ContextRoutePredicateFactory factory = context.getBean(ContextRoutePredicateFactory.class);
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/test").build());
|
||||
|
||||
// Act
|
||||
ContextRoutePredicateFactory.Config config = new ContextRoutePredicateFactory.Config();
|
||||
config.setGroup("test-group");
|
||||
|
||||
// Assert
|
||||
boolean result = factory.apply(config).test(exchange);
|
||||
assertThat(result).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportShortcutFieldOrder() {
|
||||
contextRunner.run(context -> {
|
||||
ContextRoutePredicateFactory factory = context.getBean(ContextRoutePredicateFactory.class);
|
||||
assertThat(factory.shortcutFieldOrder()).containsExactly("group");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateValidRoutePredicate() {
|
||||
contextRunner.run(context -> {
|
||||
// Arrange
|
||||
Route route = Route.async()
|
||||
.id("test-route")
|
||||
.uri("http://example.com")
|
||||
.predicate(context.getBean(ContextRoutePredicateFactory.class)
|
||||
.apply(c -> c.setGroup("group1")))
|
||||
.build();
|
||||
|
||||
// Act
|
||||
assertThat(route.getPredicate()).isNotNull();
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in new issue