Refactor LoadBalancerExchangeFilterFunction integration tests.
Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,16 +27,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.CompletionContext;
|
||||
import org.springframework.cloud.client.loadbalancer.DefaultRequestContext;
|
||||
@@ -45,51 +42,47 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.Request;
|
||||
import org.springframework.cloud.client.loadbalancer.Response;
|
||||
import org.springframework.cloud.client.loadbalancer.ResponseData;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactorLoadBalancerExchangeFilterFunction}.
|
||||
* Base class for {@link LoadBalancedExchangeFilterFunction} integration tests.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Charu Covindane
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
class ReactorLoadBalancerExchangeFilterFunctionTests {
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
abstract class AbstractLoadBalancerExchangeFilterFunctionIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private ReactorLoadBalancerExchangeFilterFunction loadBalancerFunction;
|
||||
protected LoadBalancedExchangeFilterFunction loadBalancerFunction;
|
||||
|
||||
@Autowired
|
||||
private SimpleDiscoveryProperties properties;
|
||||
protected SimpleDiscoveryProperties properties;
|
||||
|
||||
@Autowired
|
||||
private LoadBalancerProperties loadBalancerProperties;
|
||||
protected LoadBalancerProperties loadBalancerProperties;
|
||||
|
||||
@Autowired
|
||||
private ReactiveLoadBalancer.Factory<ServiceInstance> factory;
|
||||
protected ReactiveLoadBalancer.Factory<ServiceInstance> factory;
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
protected int port;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
protected void setUp() {
|
||||
DefaultServiceInstance instance = new DefaultServiceInstance();
|
||||
instance.setServiceId("testservice");
|
||||
instance.setUri(URI.create("http://localhost:" + this.port));
|
||||
instance.setUri(URI.create("http://localhost:" + port));
|
||||
DefaultServiceInstance instanceWithNoLifecycleProcessors = new DefaultServiceInstance();
|
||||
instanceWithNoLifecycleProcessors.setServiceId("serviceWithNoLifecycleProcessors");
|
||||
instanceWithNoLifecycleProcessors.setUri(URI.create("http://localhost:" + this.port));
|
||||
instanceWithNoLifecycleProcessors.setUri(URI.create("http://localhost:" + port));
|
||||
properties.getInstances().put("testservice", Collections.singletonList(instance));
|
||||
properties.getInstances()
|
||||
.put("serviceWithNoLifecycleProcessors", Collections.singletonList(instanceWithNoLifecycleProcessors));
|
||||
@@ -97,41 +90,50 @@ class ReactorLoadBalancerExchangeFilterFunctionTests {
|
||||
|
||||
@Test
|
||||
void correctResponseReturnedForExistingHostAndInstancePresent() {
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
ResponseEntity<String> response = WebClient.builder()
|
||||
.baseUrl("http://testservice")
|
||||
.filter(loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/hello")
|
||||
.exchange()
|
||||
.retrieve()
|
||||
.toEntity(String.class)
|
||||
.block();
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World");
|
||||
then(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(response.getBody()).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void serviceUnavailableReturnedWhenNoInstancePresent() {
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
.baseUrl("http://xxx")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.exchange()
|
||||
.block();
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> WebClient.builder()
|
||||
.baseUrl("http://xxx")
|
||||
.filter(loadBalancerFunction)
|
||||
.defaultStatusHandler(httpStatusCode -> httpStatusCode.equals(HttpStatus.SERVICE_UNAVAILABLE),
|
||||
clientResponse -> Mono.just(new IllegalStateException("503")))
|
||||
.build()
|
||||
.get()
|
||||
.retrieve()
|
||||
.toBodilessEntity()
|
||||
.block())
|
||||
.withMessage("503");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // FIXME 3.0.0
|
||||
void badRequestReturnedForIncorrectHost() {
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
.baseUrl("http:///xxx")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.exchange()
|
||||
.block();
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> WebClient.builder()
|
||||
.baseUrl("http:///xxx")
|
||||
.filter(loadBalancerFunction)
|
||||
.defaultStatusHandler(httpStatusCode -> httpStatusCode.equals(HttpStatus.BAD_REQUEST),
|
||||
response -> Mono.just(new IllegalStateException("400")))
|
||||
.build()
|
||||
.get()
|
||||
.retrieve()
|
||||
.toBodilessEntity()
|
||||
.block())
|
||||
.withMessage("400");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,7 +144,7 @@ class ReactorLoadBalancerExchangeFilterFunctionTests {
|
||||
.build()
|
||||
.get()
|
||||
.uri("/hello")
|
||||
.exchange()
|
||||
.exchangeToMono(clientResponse -> clientResponse.bodyToMono(String.class))
|
||||
.block()).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@@ -150,89 +152,80 @@ class ReactorLoadBalancerExchangeFilterFunctionTests {
|
||||
void loadBalancerLifecycleCallbacksExecuted() {
|
||||
final String callbackTestHint = "callbackTestHint";
|
||||
loadBalancerProperties.getHint().put("testservice", "callbackTestHint");
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
|
||||
ResponseEntity<Void> response = WebClient.builder()
|
||||
.baseUrl("http://testservice")
|
||||
.filter(loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/callback")
|
||||
.exchange()
|
||||
.retrieve()
|
||||
.toBodilessEntity()
|
||||
.block();
|
||||
|
||||
Collection<Request<Object>> lifecycleLogRequests = ((TestLoadBalancerLifecycle) factory
|
||||
.getInstances("testservice", LoadBalancerLifecycle.class)
|
||||
.get("loadBalancerLifecycle")).getStartLog().values();
|
||||
Collection<Request<Object>> lifecycleStartedLogRequests = ((TestLoadBalancerLifecycle) factory
|
||||
Collection<Request<Object>> lifecycleLogStartRequests = ((TestLoadBalancerLifecycle) factory
|
||||
.getInstances("testservice", LoadBalancerLifecycle.class)
|
||||
.get("loadBalancerLifecycle")).getStartRequestLog().values();
|
||||
Collection<CompletionContext<Object, ServiceInstance, Object>> anotherLifecycleLogRequests = ((AnotherLoadBalancerLifecycle) factory
|
||||
.getInstances("testservice", LoadBalancerLifecycle.class)
|
||||
.get("anotherLoadBalancerLifecycle")).getCompleteLog().values();
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(lifecycleLogRequests).extracting(request -> ((DefaultRequestContext) request.getContext()).getHint())
|
||||
.contains(callbackTestHint);
|
||||
assertThat(lifecycleStartedLogRequests)
|
||||
assertThat(lifecycleLogStartRequests)
|
||||
.extracting(request -> ((DefaultRequestContext) request.getContext()).getHint())
|
||||
.contains(callbackTestHint);
|
||||
assertThat(anotherLifecycleLogRequests)
|
||||
.extracting(completionContext -> ((ResponseData) completionContext.getClientResponse()).getRequestData()
|
||||
.getUrl()
|
||||
.toString())
|
||||
.contains("http://testservice/callback");
|
||||
.getHttpMethod())
|
||||
.contains(HttpMethod.GET);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@EnableDiscoveryClient
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration(proxyBeanMethods = false)
|
||||
@RestController
|
||||
static class Config {
|
||||
protected static class TestLoadBalancerFactory implements ReactiveLoadBalancer.Factory<ServiceInstance> {
|
||||
|
||||
@GetMapping("/hello")
|
||||
public String hello() {
|
||||
return "Hello World";
|
||||
private final ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.TestLoadBalancerLifecycle testLoadBalancerLifecycle;
|
||||
|
||||
private final ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.TestLoadBalancerLifecycle anotherLoadBalancerLifecycle;
|
||||
|
||||
private final DiscoveryClient discoveryClient;
|
||||
|
||||
private final LoadBalancerProperties properties;
|
||||
|
||||
public TestLoadBalancerFactory(DiscoveryClient discoveryClient, LoadBalancerProperties properties) {
|
||||
this.discoveryClient = discoveryClient;
|
||||
this.properties = properties;
|
||||
testLoadBalancerLifecycle = new ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.TestLoadBalancerLifecycle();
|
||||
anotherLoadBalancerLifecycle = new ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.AnotherLoadBalancerLifecycle();
|
||||
}
|
||||
|
||||
@GetMapping("/callback")
|
||||
String callbackTestResult() {
|
||||
return "callbackTestResult";
|
||||
@Override
|
||||
public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
|
||||
return new DiscoveryClientBasedReactiveLoadBalancer(serviceId, discoveryClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveLoadBalancer.Factory<ServiceInstance> reactiveLoadBalancerFactory(DiscoveryClient discoveryClient,
|
||||
LoadBalancerProperties properties) {
|
||||
return new ReactiveLoadBalancer.Factory<>() {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public <X> Map<String, X> getInstances(String name, Class<X> type) {
|
||||
if (name.equals("serviceWithNoLifecycleProcessors")) {
|
||||
return null;
|
||||
}
|
||||
Map lifecycleProcessors = new HashMap<>();
|
||||
lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle);
|
||||
lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle);
|
||||
return lifecycleProcessors;
|
||||
}
|
||||
|
||||
private final TestLoadBalancerLifecycle testLoadBalancerLifecycle = new TestLoadBalancerLifecycle();
|
||||
@Override
|
||||
public <X> X getInstance(String name, Class<?> clazz, Class<?>... generics) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private final TestLoadBalancerLifecycle anotherLoadBalancerLifecycle = new AnotherLoadBalancerLifecycle();
|
||||
|
||||
@Override
|
||||
public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
|
||||
return new DiscoveryClientBasedReactiveLoadBalancer(serviceId, discoveryClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> Map<String, X> getInstances(String name, Class<X> type) {
|
||||
if (name.equals("serviceWithNoLifecycleProcessors")) {
|
||||
return null;
|
||||
}
|
||||
Map lifecycleProcessors = new HashMap<>();
|
||||
lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle);
|
||||
lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle);
|
||||
return lifecycleProcessors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X getInstance(String name, Class<?> clazz, Class<?>... generics) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancerProperties getProperties(String serviceId) {
|
||||
return properties;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public LoadBalancerProperties getProperties(String serviceId) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -257,6 +250,7 @@ class ReactorLoadBalancerExchangeFilterFunctionTests {
|
||||
|
||||
@Override
|
||||
public void onComplete(CompletionContext<Object, ServiceInstance, Object> completionContext) {
|
||||
completeLog.clear();
|
||||
completeLog.put(getName() + UUID.randomUUID(), completionContext);
|
||||
}
|
||||
|
||||
@@ -273,18 +267,13 @@ class ReactorLoadBalancerExchangeFilterFunctionTests {
|
||||
}
|
||||
|
||||
protected String getName() {
|
||||
return this.getClass().getSimpleName();
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class AnotherLoadBalancerLifecycle extends TestLoadBalancerLifecycle {
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springframework.cloud.client.loadbalancer.reactive;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactorLoadBalancerExchangeFilterFunction}.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Charu Covindane
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
class ReactorLoadBalancerExchangeFilterFunctionIntegrationTests
|
||||
extends AbstractLoadBalancerExchangeFilterFunctionIntegrationTests {
|
||||
|
||||
@EnableDiscoveryClient
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration(proxyBeanMethods = false)
|
||||
@RestController
|
||||
static class Config {
|
||||
|
||||
@GetMapping("/hello")
|
||||
public String hello() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
@GetMapping("/callback")
|
||||
String callbackTestResult() {
|
||||
return "callbackTestResult";
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveLoadBalancer.Factory<ServiceInstance> reactiveLoadBalancerFactory(DiscoveryClient discoveryClient,
|
||||
LoadBalancerProperties properties) {
|
||||
return new TestLoadBalancerFactory(discoveryClient, properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,45 +18,27 @@ package org.springframework.cloud.client.loadbalancer.reactive;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.CompletionContext;
|
||||
import org.springframework.cloud.client.loadbalancer.DefaultRequestContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.Request;
|
||||
import org.springframework.cloud.client.loadbalancer.Response;
|
||||
import org.springframework.cloud.client.loadbalancer.ResponseData;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
@@ -66,103 +48,28 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private RetryableLoadBalancerExchangeFilterFunction loadBalancerFunction;
|
||||
|
||||
@Autowired
|
||||
private SimpleDiscoveryProperties properties;
|
||||
|
||||
@Autowired
|
||||
private LoadBalancerProperties loadBalancerProperties;
|
||||
|
||||
@Autowired
|
||||
private ReactiveLoadBalancer.Factory<ServiceInstance> factory;
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
DefaultServiceInstance instance = new DefaultServiceInstance();
|
||||
instance.setServiceId("testservice");
|
||||
instance.setUri(URI.create("http://localhost:" + port));
|
||||
DefaultServiceInstance instanceWithNoLifecycleProcessors = new DefaultServiceInstance();
|
||||
instanceWithNoLifecycleProcessors.setServiceId("serviceWithNoLifecycleProcessors");
|
||||
instanceWithNoLifecycleProcessors.setUri(URI.create("http://localhost:" + port));
|
||||
properties.getInstances().put("testservice", Collections.singletonList(instance));
|
||||
properties.getInstances()
|
||||
.put("serviceWithNoLifecycleProcessors", Collections.singletonList(instanceWithNoLifecycleProcessors));
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadBalancerLifecycleCallbacksExecuted() {
|
||||
final String callbackTestHint = "callbackTestHint";
|
||||
loadBalancerProperties.getHint().put("testservice", "callbackTestHint");
|
||||
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
.baseUrl("http://testservice")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/callback")
|
||||
.exchange()
|
||||
.block();
|
||||
|
||||
Collection<Request<Object>> lifecycleLogRequests = ((TestLoadBalancerLifecycle) factory
|
||||
.getInstances("testservice", LoadBalancerLifecycle.class)
|
||||
.get("loadBalancerLifecycle")).getStartLog().values();
|
||||
Collection<Request<Object>> lifecycleLogStartRequests = ((TestLoadBalancerLifecycle) factory
|
||||
.getInstances("testservice", LoadBalancerLifecycle.class)
|
||||
.get("loadBalancerLifecycle")).getStartRequestLog().values();
|
||||
Collection<CompletionContext<Object, ServiceInstance, Object>> anotherLifecycleLogRequests = ((AnotherLoadBalancerLifecycle) factory
|
||||
.getInstances("testservice", LoadBalancerLifecycle.class)
|
||||
.get("anotherLoadBalancerLifecycle")).getCompleteLog().values();
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(lifecycleLogRequests).extracting(request -> ((DefaultRequestContext) request.getContext()).getHint())
|
||||
.contains(callbackTestHint);
|
||||
assertThat(lifecycleLogStartRequests)
|
||||
.extracting(request -> ((DefaultRequestContext) request.getContext()).getHint())
|
||||
.contains(callbackTestHint);
|
||||
assertThat(anotherLifecycleLogRequests)
|
||||
.extracting(completionContext -> ((ResponseData) completionContext.getClientResponse()).getRequestData()
|
||||
.getHttpMethod())
|
||||
.contains(HttpMethod.GET);
|
||||
}
|
||||
|
||||
@Test
|
||||
void correctResponseReturnedForExistingHostAndInstancePresent() {
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
.baseUrl("http://testservice")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/hello")
|
||||
.exchange()
|
||||
.block();
|
||||
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World");
|
||||
}
|
||||
class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests
|
||||
extends AbstractLoadBalancerExchangeFilterFunctionIntegrationTests {
|
||||
|
||||
@Test
|
||||
void correctResponseReturnedAfterRetryingOnSameServiceInstance() {
|
||||
loadBalancerProperties.getRetry().setMaxRetriesOnSameServiceInstance(1);
|
||||
loadBalancerProperties.getRetry().getRetryableStatusCodes().add(500);
|
||||
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
ResponseEntity<String> response = WebClient.builder()
|
||||
.baseUrl("http://testservice")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.filter(loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/exception")
|
||||
.exchange()
|
||||
.retrieve()
|
||||
.toEntity(String.class)
|
||||
.block();
|
||||
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World!");
|
||||
then(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(response.getBody()).isEqualTo("Hello World!");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -178,72 +85,33 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests {
|
||||
properties.getInstances().put("retrytest", Arrays.asList(badRetryTestInstance, goodRetryTestInstance));
|
||||
loadBalancerProperties.getRetry().getRetryableStatusCodes().add(500);
|
||||
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
ResponseEntity<String> response = WebClient.builder()
|
||||
.baseUrl("http://retrytest")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.filter(loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/hello")
|
||||
.exchange()
|
||||
.retrieve()
|
||||
.toEntity(String.class)
|
||||
.block();
|
||||
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World");
|
||||
then(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(response.getBody()).isEqualTo("Hello World");
|
||||
|
||||
ClientResponse secondClientResponse = WebClient.builder()
|
||||
ResponseEntity<String> secondResponse = WebClient.builder()
|
||||
.baseUrl("http://retrytest")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.filter(loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/hello")
|
||||
.exchange()
|
||||
.retrieve()
|
||||
.toEntity(String.class)
|
||||
.block();
|
||||
|
||||
then(secondClientResponse.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(secondClientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World");
|
||||
then(secondResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
then(secondResponse.getBody()).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void serviceUnavailableReturnedWhenNoInstancePresent() {
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
.baseUrl("http://xxx")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.exchange()
|
||||
.block();
|
||||
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
// FIXME 3.0.0
|
||||
void badRequestReturnedForIncorrectHost() {
|
||||
ClientResponse clientResponse = WebClient.builder()
|
||||
.baseUrl("http:///xxx")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.exchange()
|
||||
.block();
|
||||
|
||||
then(clientResponse.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exceptionNotThrownWhenFactoryReturnsNullLifecycleProcessorsMap() {
|
||||
assertThatCode(() -> WebClient.builder()
|
||||
.baseUrl("http://serviceWithNoLifecycleProcessors")
|
||||
.filter(this.loadBalancerFunction)
|
||||
.build()
|
||||
.get()
|
||||
.uri("/hello")
|
||||
.exchange()
|
||||
.block()).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@EnableDiscoveryClient
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration(proxyBeanMethods = false)
|
||||
@@ -274,42 +142,11 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests {
|
||||
@Bean
|
||||
ReactiveLoadBalancer.Factory<ServiceInstance> reactiveLoadBalancerFactory(DiscoveryClient discoveryClient,
|
||||
LoadBalancerProperties properties) {
|
||||
return new ReactiveLoadBalancer.Factory<>() {
|
||||
|
||||
private final TestLoadBalancerLifecycle testLoadBalancerLifecycle = new TestLoadBalancerLifecycle();
|
||||
|
||||
private final TestLoadBalancerLifecycle anotherLoadBalancerLifecycle = new AnotherLoadBalancerLifecycle();
|
||||
|
||||
@Override
|
||||
public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
|
||||
return new org.springframework.cloud.client.loadbalancer.reactive.DiscoveryClientBasedReactiveLoadBalancer(
|
||||
serviceId, discoveryClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> Map<String, X> getInstances(String name, Class<X> type) {
|
||||
if (name.equals("serviceWithNoLifecycleProcessors")) {
|
||||
return null;
|
||||
}
|
||||
Map lifecycleProcessors = new HashMap<>();
|
||||
lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle);
|
||||
lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle);
|
||||
return lifecycleProcessors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X getInstance(String name, Class<?> clazz, Class<?>... generics) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancerProperties getProperties(String serviceId) {
|
||||
return properties;
|
||||
}
|
||||
};
|
||||
return new TestLoadBalancerFactory(discoveryClient, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
RetryableLoadBalancerExchangeFilterFunction exchangeFilterFunction(
|
||||
ReactiveLoadBalancer.Factory<ServiceInstance> factory) {
|
||||
return new RetryableLoadBalancerExchangeFilterFunction(
|
||||
@@ -319,55 +156,4 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests {
|
||||
|
||||
}
|
||||
|
||||
protected static class TestLoadBalancerLifecycle implements LoadBalancerLifecycle<Object, Object, ServiceInstance> {
|
||||
|
||||
Map<String, Request<Object>> startLog = new ConcurrentHashMap<>();
|
||||
|
||||
Map<String, Request<Object>> startRequestLog = new ConcurrentHashMap<>();
|
||||
|
||||
Map<String, CompletionContext<Object, ServiceInstance, Object>> completeLog = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void onStart(Request<Object> request) {
|
||||
startLog.put(getName() + UUID.randomUUID(), request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartRequest(Request<Object> request, Response<ServiceInstance> lbResponse) {
|
||||
startRequestLog.put(getName() + UUID.randomUUID(), request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(CompletionContext<Object, ServiceInstance, Object> completionContext) {
|
||||
completeLog.clear();
|
||||
completeLog.put(getName() + UUID.randomUUID(), completionContext);
|
||||
}
|
||||
|
||||
Map<String, Request<Object>> getStartLog() {
|
||||
return startLog;
|
||||
}
|
||||
|
||||
Map<String, CompletionContext<Object, ServiceInstance, Object>> getCompleteLog() {
|
||||
return completeLog;
|
||||
}
|
||||
|
||||
Map<String, Request<Object>> getStartRequestLog() {
|
||||
return startRequestLog;
|
||||
}
|
||||
|
||||
protected String getName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class AnotherLoadBalancerLifecycle extends TestLoadBalancerLifecycle {
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user