diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java index 8c320190..2bd21ab6 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java @@ -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. @@ -29,6 +29,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.ClientRequest; @@ -80,7 +81,7 @@ public class RequestData { } private static MultiValueMap buildCookies(MultiValueMap cookies) { - HttpHeaders newCookies = new HttpHeaders(); + MultiValueMap newCookies = new LinkedMultiValueMap<>(); if (cookies != null) { cookies.forEach((key, value) -> value .forEach(cookie -> newCookies.put(cookie.getName(), Collections.singletonList(cookie.getValue())))); @@ -89,7 +90,7 @@ public class RequestData { } private static MultiValueMap buildCookiesFromHeaders(HttpHeaders headers) { - HttpHeaders newCookies = new HttpHeaders(); + MultiValueMap newCookies = new LinkedMultiValueMap<>(); if (headers == null) { return newCookies; } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java index a5663ab7..27cb50da 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java @@ -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. @@ -22,6 +22,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Marcin Grzejszczak + * @author Olga Maciaszek-Sharma */ @ConfigurationProperties("spring.cloud.compatibility-verifier") public class CompatibilityVerifierProperties { @@ -36,7 +37,7 @@ public class CompatibilityVerifierProperties { * the patch version if you don't want to specify a concrete value. Example: * {@code 3.5.x} */ - private List compatibleBootVersions = List.of("3.5.x"); + private List compatibleBootVersions = List.of("4.0.x"); public boolean isEnabled() { return this.enabled; diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java index 880f49f9..f1e6cb69 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java @@ -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,7 +27,7 @@ import org.springframework.boot.SpringBootVersion; import org.springframework.util.StringUtils; /** - * Verifies if Spring Boot has proper version. + * Verifies if Spring Boot has the proper version. */ class SpringBootVersionVerifier implements CompatibilityVerifier { @@ -35,7 +35,7 @@ class SpringBootVersionVerifier implements CompatibilityVerifier { final Map ACCEPTED_VERSIONS = new HashMap<>() { { - this.put("3.5", is3_5()); + this.put("4.0", is4_0()); } }; @@ -70,12 +70,12 @@ class SpringBootVersionVerifier implements CompatibilityVerifier { return SpringBootVersion.getVersion(); } - CompatibilityPredicate is3_5() { + CompatibilityPredicate is4_0() { return new CompatibilityPredicate() { @Override public String toString() { - return "Predicate for Boot 3.5"; + return "Predicate for Boot 4.0"; } @Override diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java index 62205271..3f3f2936 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java @@ -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. @@ -29,7 +29,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.AfterEach; -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; @@ -56,6 +55,9 @@ import org.springframework.retry.backoff.NoBackOffPolicy; import org.springframework.retry.listener.RetryListenerSupport; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIOException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.BDDAssertions.then; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -117,7 +119,7 @@ public class RetryLoadBalancerInterceptorTests { when(lbRequestFactory.createRequest(any(), any(), any())).thenReturn(mock(LoadBalancerRequest.class)); - Assertions.assertThrows(IOException.class, () -> interceptor.intercept(request, body, execution)); + assertThatIOException().isThrownBy(() -> interceptor.intercept(request, body, execution)); verify(lbRequestFactory).createRequest(request, body, execution); } @@ -130,7 +132,7 @@ public class RetryLoadBalancerInterceptorTests { loadBalancedRetryFactory, lbFactory); byte[] body = new byte[] {}; ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class); - Assertions.assertThrows(IllegalStateException.class, () -> interceptor.intercept(request, body, execution)); + assertThatIllegalStateException().isThrownBy(() -> interceptor.intercept(request, body, execution)); } @Test @@ -288,7 +290,7 @@ public class RetryLoadBalancerInterceptorTests { new MyLoadBalancedRetryFactory(policy), lbFactory); byte[] body = new byte[] {}; ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class); - Assertions.assertThrows(IOException.class, () -> interceptor.intercept(request, body, execution)); + assertThatIOException().isThrownBy(() -> interceptor.intercept(request, body, execution)); verify(lbRequestFactory).createRequest(request, body, execution); } @@ -370,8 +372,8 @@ public class RetryLoadBalancerInterceptorTests { new MyLoadBalancedRetryFactory(policy, backOffPolicy, new RetryListener[] { myRetryListener }), lbFactory); ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class); - Assertions.assertThrows(TerminatedRetryException.class, - () -> interceptor.intercept(request, new byte[] {}, execution)); + assertThatExceptionOfType(TerminatedRetryException.class) + .isThrownBy(() -> interceptor.intercept(request, new byte[] {}, execution)); } @Test diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java similarity index 58% rename from spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionTests.java rename to spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java index d5f1c291..06cf64a4 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -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. @@ -25,18 +25,14 @@ import java.util.UUID; 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 +41,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 factory; + protected ReactiveLoadBalancer.Factory 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 +89,49 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { @Test void correctResponseReturnedForExistingHostAndInstancePresent() { - ClientResponse clientResponse = WebClient.builder() + ResponseEntity 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 +142,7 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { .build() .get() .uri("/hello") - .exchange() + .exchangeToMono(clientResponse -> clientResponse.bodyToMono(String.class)) .block()).doesNotThrowAnyException(); } @@ -150,89 +150,80 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { void loadBalancerLifecycleCallbacksExecuted() { final String callbackTestHint = "callbackTestHint"; loadBalancerProperties.getHint().put("testservice", "callbackTestHint"); - ClientResponse clientResponse = WebClient.builder() + + ResponseEntity response = WebClient.builder() .baseUrl("http://testservice") .filter(loadBalancerFunction) .build() .get() .uri("/callback") - .exchange() + .retrieve() + .toBodilessEntity() .block(); Collection> lifecycleLogRequests = ((TestLoadBalancerLifecycle) factory .getInstances("testservice", LoadBalancerLifecycle.class) .get("loadBalancerLifecycle")).getStartLog().values(); - Collection> lifecycleStartedLogRequests = ((TestLoadBalancerLifecycle) factory + Collection> lifecycleLogStartRequests = ((TestLoadBalancerLifecycle) factory .getInstances("testservice", LoadBalancerLifecycle.class) .get("loadBalancerLifecycle")).getStartRequestLog().values(); Collection> 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 { - @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 getInstance(String serviceId) { + return new DiscoveryClientBasedReactiveLoadBalancer(serviceId, discoveryClient); } - @Bean - ReactiveLoadBalancer.Factory reactiveLoadBalancerFactory(DiscoveryClient discoveryClient, - LoadBalancerProperties properties) { - return new ReactiveLoadBalancer.Factory<>() { + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Map getInstances(String name, Class 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 getInstance(String name, Class clazz, Class... generics) { + return null; + } - private final TestLoadBalancerLifecycle anotherLoadBalancerLifecycle = new AnotherLoadBalancerLifecycle(); - - @Override - public ReactiveLoadBalancer getInstance(String serviceId) { - return new DiscoveryClientBasedReactiveLoadBalancer(serviceId, discoveryClient); - } - - @Override - public Map getInstances(String name, Class type) { - if (name.equals("serviceWithNoLifecycleProcessors")) { - return null; - } - Map lifecycleProcessors = new HashMap<>(); - lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle); - lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle); - return lifecycleProcessors; - } - - @Override - public 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 +248,7 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { @Override public void onComplete(CompletionContext completionContext) { + completeLog.clear(); completeLog.put(getName() + UUID.randomUUID(), completionContext); } @@ -273,18 +265,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(); - } - } } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.java new file mode 100644 index 00000000..5b83151f --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -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 reactiveLoadBalancerFactory(DiscoveryClient discoveryClient, + LoadBalancerProperties properties) { + return new TestLoadBalancerFactory(discoveryClient, properties); + } + + } + +} diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java index 6bfbf735..9ef145e6 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -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,107 +48,31 @@ 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 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> lifecycleLogRequests = ((TestLoadBalancerLifecycle) factory - .getInstances("testservice", LoadBalancerLifecycle.class) - .get("loadBalancerLifecycle")).getStartLog().values(); - Collection> lifecycleLogStartRequests = ((TestLoadBalancerLifecycle) factory - .getInstances("testservice", LoadBalancerLifecycle.class) - .get("loadBalancerLifecycle")).getStartRequestLog().values(); - Collection> 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 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 - @Disabled void correctResponseReturnedAfterRetryingOnNextServiceInstanceWithBackoff() { loadBalancerProperties.getRetry().getBackoff().setEnabled(true); loadBalancerProperties.getRetry().setMaxRetriesOnSameServiceInstance(1); @@ -179,72 +85,33 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests { properties.getInstances().put("retrytest", Arrays.asList(badRetryTestInstance, goodRetryTestInstance)); loadBalancerProperties.getRetry().getRetryableStatusCodes().add(500); - ClientResponse clientResponse = WebClient.builder() + ResponseEntity 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 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) @@ -275,42 +142,11 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests { @Bean ReactiveLoadBalancer.Factory reactiveLoadBalancerFactory(DiscoveryClient discoveryClient, LoadBalancerProperties properties) { - return new ReactiveLoadBalancer.Factory<>() { - - private final TestLoadBalancerLifecycle testLoadBalancerLifecycle = new TestLoadBalancerLifecycle(); - - private final TestLoadBalancerLifecycle anotherLoadBalancerLifecycle = new AnotherLoadBalancerLifecycle(); - - @Override - public ReactiveLoadBalancer getInstance(String serviceId) { - return new org.springframework.cloud.client.loadbalancer.reactive.DiscoveryClientBasedReactiveLoadBalancer( - serviceId, discoveryClient); - } - - @Override - public Map getInstances(String name, Class type) { - if (name.equals("serviceWithNoLifecycleProcessors")) { - return null; - } - Map lifecycleProcessors = new HashMap<>(); - lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle); - lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle); - return lifecycleProcessors; - } - - @Override - public 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 factory) { return new RetryableLoadBalancerExchangeFilterFunction( @@ -320,55 +156,4 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests { } - protected static class TestLoadBalancerLifecycle implements LoadBalancerLifecycle { - - Map> startLog = new ConcurrentHashMap<>(); - - Map> startRequestLog = new ConcurrentHashMap<>(); - - Map> completeLog = new ConcurrentHashMap<>(); - - @Override - public void onStart(Request request) { - startLog.put(getName() + UUID.randomUUID(), request); - } - - @Override - public void onStartRequest(Request request, Response lbResponse) { - startRequestLog.put(getName() + UUID.randomUUID(), request); - } - - @Override - public void onComplete(CompletionContext completionContext) { - completeLog.clear(); - completeLog.put(getName() + UUID.randomUUID(), completionContext); - } - - Map> getStartLog() { - return startLog; - } - - Map> getCompleteLog() { - return completeLog; - } - - Map> getStartRequestLog() { - return startRequestLog; - } - - protected String getName() { - return this.getClass().getSimpleName(); - } - - } - - protected static class AnotherLoadBalancerLifecycle extends TestLoadBalancerLifecycle { - - @Override - protected String getName() { - return this.getClass().getSimpleName(); - } - - } - } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java index 29bef5bc..5c84e5c4 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java @@ -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. @@ -31,6 +31,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFunction; @@ -75,7 +76,7 @@ class RetryableLoadBalancerExchangeFilterFunctionTests { when(factory.getInstance("test")).thenReturn(new TestReactiveLoadBalancer()); when(factory.getProperties(any())).thenReturn(properties); when(clientRequest.headers()).thenReturn(new HttpHeaders()); - when(clientRequest.cookies()).thenReturn(new HttpHeaders()); + when(clientRequest.cookies()).thenReturn(new LinkedMultiValueMap<>()); } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java index 825ddc6a..0bc8910a 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java @@ -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,6 +27,7 @@ import static org.assertj.core.api.BDDAssertions.then; /** * @author Marcin Grzejszczak + * @author Olga Maciaszek-Sharma */ public class SpringBootDependencyTests { @@ -176,8 +177,8 @@ public class SpringBootDependencyTests { @Test public void should_match_against_current_manifest() { try { - verifyCurrentVersionFromManifest("3.5"); - verifyCurrentVersionFromManifest("3.5.x"); + verifyCurrentVersionFromManifest("4.0"); + verifyCurrentVersionFromManifest("4.0.x"); } catch (AssertionError e) { // if (e.getMessage() != null && e.getMessage().contains("3.3.")) { @@ -204,7 +205,7 @@ public class SpringBootDependencyTests { @Test public void should_match_against_current_predicate() { - List acceptedVersions = Collections.singletonList("3.0"); + List acceptedVersions = Collections.singletonList("4.0"); SpringBootVersionVerifier versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { @Override String getVersionFromManifest() { @@ -212,7 +213,7 @@ public class SpringBootDependencyTests { } }; versionVerifier.ACCEPTED_VERSIONS.clear(); - versionVerifier.ACCEPTED_VERSIONS.put("3.0", versionVerifier.is3_5()); + versionVerifier.ACCEPTED_VERSIONS.put("4.0", versionVerifier.is4_0()); VerificationResult verificationResult = versionVerifier.verify(); @@ -222,7 +223,7 @@ public class SpringBootDependencyTests { @Test public void should_match_against_current_predicate_with_version_ending_with_x() { - List acceptedVersions = Collections.singletonList("3.0.x"); + List acceptedVersions = Collections.singletonList("4.0.x"); SpringBootVersionVerifier versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { @Override String getVersionFromManifest() { @@ -230,7 +231,7 @@ public class SpringBootDependencyTests { } }; versionVerifier.ACCEPTED_VERSIONS.clear(); - versionVerifier.ACCEPTED_VERSIONS.put("3.0", versionVerifier.is3_5()); + versionVerifier.ACCEPTED_VERSIONS.put("4.0", versionVerifier.is4_0()); VerificationResult verificationResult = versionVerifier.verify(); diff --git a/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java b/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java index 57cc81f3..fa6fb5e7 100644 --- a/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java +++ b/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 the original author or authors. + * Copyright 2013-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. @@ -19,7 +19,6 @@ package org.springframework.cloud.context.integration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,6 +41,7 @@ import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.test.annotation.DirtiesContext; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.BDDAssertions.then; @SpringBootTest(classes = TestConfiguration.class) @@ -71,12 +71,12 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testSimpleProperties() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - then(this.service instanceof Advised).isTrue(); + then(service.getMessage()).isEqualTo("Hello scope!"); + then(service instanceof Advised).isTrue(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...but don't refresh, so the bean stays the same: - then(this.service.getMessage()).isEqualTo("Hello scope!"); + then(service.getMessage()).isEqualTo("Hello scope!"); then(ExampleService.getInitCount()).isEqualTo(0); then(ExampleService.getDestroyCount()).isEqualTo(0); } @@ -84,14 +84,14 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefresh() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refreshAll(); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refreshAll(); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -102,15 +102,15 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefreshBean() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refresh("service"); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refresh("service"); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -122,7 +122,7 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testCheckedException() { - Assertions.assertThrows(ServiceException.class, () -> this.service.throwsException()); + assertThatExceptionOfType(ServiceException.class).isThrownBy(() -> service.throwsException()); } public interface Service { @@ -168,28 +168,28 @@ public class RefreshScopeIntegrationTests { @Override public void afterPropertiesSet() { - logger.debug("Initializing message: " + this.message); + logger.debug("Initializing message: " + message); initCount++; } @Override public void destroy() { - logger.debug("Destroying message: " + this.message); + logger.debug("Destroying message: " + message); destroyCount++; - this.message = null; + message = null; } @Override public String getMessage() { - logger.debug("Getting message: " + this.message); + logger.debug("Getting message: " + message); try { - Thread.sleep(this.delay); + Thread.sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - logger.info("Returning message: " + this.message); - return this.message; + logger.info("Returning message: " + message); + return message; } public void setMessage(String message) { @@ -226,8 +226,8 @@ public class RefreshScopeIntegrationTests { @RefreshScope public ExampleService service() { ExampleService service = new ExampleService(); - service.setMessage(this.properties.getMessage()); - service.setDelay(this.properties.getDelay()); + service.setMessage(properties.getMessage()); + service.setDelay(properties.getDelay()); return service; } @@ -243,7 +243,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public String getMessage() { - return this.message; + return message; } public void setMessage(String message) { @@ -252,7 +252,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public int getDelay() { - return this.delay; + return delay; } public void setDelay(int delay) { diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java index b75a564f..449c390e 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java @@ -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. @@ -57,7 +57,6 @@ import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -67,6 +66,7 @@ import org.springframework.util.StringUtils; *

* * @author Dave Syer + * @author Yanming Zhou * @since 3.1 * */ @@ -81,13 +81,11 @@ public class GenericScope private ConfigurableListableBeanFactory beanFactory; - private StandardEvaluationContext evaluationContext; - private String id; - private Map errors = new ConcurrentHashMap<>(); + private final Map errors = new ConcurrentHashMap<>(); - private ConcurrentMap locks = new ConcurrentHashMap<>(); + private final ConcurrentMap locks = new ConcurrentHashMap<>(); static RuntimeException wrapIfNecessary(Throwable throwable) { if (throwable instanceof RuntimeException) { @@ -213,7 +211,7 @@ public class GenericScope @Override public Object resolveContextualObject(String key) { Expression expression = parseExpression(key); - return expression.getValue(this.evaluationContext, this.beanFactory); + return expression.getValue(this.beanFactory); } private Expression parseExpression(String input) { diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java index 94b4b466..e4e50d01 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -45,12 +44,13 @@ import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.env.StandardEnvironment; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; import static org.assertj.core.api.BDDAssertions.then; /** * @author Dave Syer * @author Yanming Zhou - * + * @author Olga Maciaszek-Sharma */ public class BootstrapConfigurationTests { @@ -68,11 +68,11 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.clear(); CompositePropertySourceConfiguration.MAP1.clear(); CompositePropertySourceConfiguration.MAP2.clear(); - if (this.context != null) { - this.context.close(); + if (context != null) { + context.close(); } - if (this.sibling != null) { - this.sibling.close(); + if (sibling != null) { + sibling.close(); } } @@ -91,13 +91,13 @@ public class BootstrapConfigurationTests { } private void pickupOnlyExternalBootstrapProperties(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); - then(this.context.getEnvironment().getProperty("info.desc")).isNull(); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); + then(context.getEnvironment().getProperty("info.desc")).isNull(); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -119,13 +119,13 @@ public class BootstrapConfigurationTests { } private void pickupAdditionalExternalBootstrapProperties(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); - then(this.context.getEnvironment().getProperty("info.desc")).isEqualTo("defaultPropertiesInfoDesc"); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); + then(context.getEnvironment().getProperty("info.desc")).isEqualTo("defaultPropertiesInfoDesc"); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -142,7 +142,7 @@ public class BootstrapConfigurationTests { } private void bootstrapPropertiesAvailableInInitializer(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .initializers(applicationContext -> { @@ -150,7 +150,7 @@ public class BootstrapConfigurationTests { then(applicationContext.getEnvironment().getProperty("info.name")).isEqualTo("child"); }) .run(); - then(this.context.getEnvironment() + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -178,12 +178,12 @@ public class BootstrapConfigurationTests { private void picksUpAdditionalPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -201,13 +201,12 @@ public class BootstrapConfigurationTests { private void failsOnPropertySource(String... properties) { System.setProperty("expected.fail", "true"); - Throwable throwable = Assertions.assertThrows(RuntimeException.class, () -> { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + assertThatRuntimeException() + .isThrownBy(() -> context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) - .run(); - }); - then(throwable.getMessage().equals("Planned")); + .run()) + .withMessage("Planned"); } @Test @@ -226,11 +225,11 @@ public class BootstrapConfigurationTests { private void overrideSystemPropertySourceByDefault(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); } @Test @@ -248,11 +247,11 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideSystemProperties", "false"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); } @Test @@ -274,11 +273,11 @@ public class BootstrapConfigurationTests { // their own remote property source. PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "false"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); } @Test @@ -297,11 +296,11 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideSystemProperties", "false"); PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "true"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); } @Test @@ -322,12 +321,12 @@ public class BootstrapConfigurationTests { ConfigurableEnvironment environment = new StandardEnvironment(); environment.getPropertySources() .addLast(new MapPropertySource("last", Collections.singletonMap("bootstrap.foo", "splat"))); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .environment(environment) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("splat"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("splat"); } @Test @@ -345,18 +344,18 @@ public class BootstrapConfigurationTests { private void applicationNameInBootstrapAndMain(String... properties) { System.setProperty("expected.name", "main"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("app"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("app"); // The parent is called "main" because spring.application.name is specified in // other.properties (the bootstrap properties) - then(this.context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); // The bootstrap context has the same "bootstrap" property source - then(((ConfigurableEnvironment) this.context.getParent().getEnvironment()).getPropertySources() - .get("bootstrap")).isEqualTo(this.context.getEnvironment().getPropertySources().get("bootstrap")); - then(this.context.getId()).isEqualTo("main-1"); + then(((ConfigurableEnvironment) context.getParent().getEnvironment()).getPropertySources().get("bootstrap")) + .isEqualTo(context.getEnvironment().getPropertySources().get("bootstrap")); + then(context.getId()).isEqualTo("main-1"); } @Test @@ -374,14 +373,14 @@ public class BootstrapConfigurationTests { private void applicationNameNotInBootstrap(String... properties) { System.setProperty("expected.name", "main"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); // The parent has no name because spring.application.name is not // defined in the bootstrap properties - then(this.context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo(null); + then(context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo(null); } @Test @@ -397,17 +396,17 @@ public class BootstrapConfigurationTests { private void applicationNameOnlyInBootstrap(String... properties) { System.setProperty("expected.name", "main"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); // The main context is called "main" because spring.application.name is specified // in other.properties (and not in the main config file) - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); // The parent is called "main" because spring.application.name is specified in // other.properties (the bootstrap properties this time) - then(this.context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); - then(this.context.getId()).isEqualTo("main-1"); + then(context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getId()).isEqualTo("main-1"); } @Test @@ -423,15 +422,15 @@ public class BootstrapConfigurationTests { private void environmentEnrichedOnceWhenSharedWithChildContext(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .environment(new StandardEnvironment()) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); - then(this.context.getParent().getEnvironment()).isEqualTo(this.context.getEnvironment()); - MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getParent().getEnvironment()).isEqualTo(context.getEnvironment()); + MutablePropertySources sources = context.getEnvironment().getPropertySources(); PropertySource bootstrap = sources .get(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap"); then(bootstrap).isNotNull(); @@ -452,16 +451,16 @@ public class BootstrapConfigurationTests { private void onlyOneBootstrapContext(String... properties) { TestHigherPriorityBootstrapConfiguration.count.set(0); PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); then(TestHigherPriorityBootstrapConfiguration.count.get()).isEqualTo(1); - then(this.context.getParent()).isNotNull(); - then(this.context.getParent().getParent().getId()).isEqualTo("bootstrap"); - then(this.context.getParent().getParent().getParent()).isNull(); - then(this.context.getEnvironment().getProperty("custom.foo")).isEqualTo("bar"); + then(context.getParent()).isNotNull(); + then(context.getParent().getParent().getId()).isEqualTo("bootstrap"); + then(context.getParent().getParent().getParent()).isNull(); + then(context.getEnvironment().getProperty("custom.foo")).isEqualTo("bar"); } @Test @@ -476,13 +475,13 @@ public class BootstrapConfigurationTests { } private void listOverride(String... properties) { - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); ListProperties listProperties = new ListProperties(); - Binder.get(this.context.getEnvironment()).bind("list", Bindable.ofInstance(listProperties)); + Binder.get(context.getEnvironment()).bind("list", Bindable.ofInstance(listProperties)); then(listProperties.getFoo().size()).isEqualTo(1); then(listProperties.getFoo().get(0)).isEqualTo("hello world"); } @@ -503,25 +502,25 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); SpringApplicationBuilder builder = new SpringApplicationBuilder().properties(properties) .sources(BareConfiguration.class); - this.sibling = builder.child(BareConfiguration.class) + sibling = builder.child(BareConfiguration.class) .properties("spring.application.name=sibling") .web(WebApplicationType.NONE) .run(); - this.context = builder.child(BareConfiguration.class) + context = builder.child(BareConfiguration.class) .properties("spring.application.name=context") .web(WebApplicationType.NONE) .run(); then(TestHigherPriorityBootstrapConfiguration.count.get()).isEqualTo(1); - then(this.context.getParent()).isNotNull(); - then(this.context.getParent().getParent().getId()).isEqualTo("bootstrap"); - then(this.context.getParent().getParent().getParent()).isNull(); - then(this.context.getEnvironment().getProperty("custom.foo")).isEqualTo("context"); - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("context"); - then(this.sibling.getParent()).isNotNull(); - then(this.sibling.getParent().getParent().getId()).isEqualTo("bootstrap"); - then(this.sibling.getParent().getParent().getParent()).isNull(); - then(this.sibling.getEnvironment().getProperty("custom.foo")).isEqualTo("sibling"); - then(this.sibling.getEnvironment().getProperty("spring.application.name")).isEqualTo("sibling"); + then(context.getParent()).isNotNull(); + then(context.getParent().getParent().getId()).isEqualTo("bootstrap"); + then(context.getParent().getParent().getParent()).isNull(); + then(context.getEnvironment().getProperty("custom.foo")).isEqualTo("context"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("context"); + then(sibling.getParent()).isNotNull(); + then(sibling.getParent().getParent().getId()).isEqualTo("bootstrap"); + then(sibling.getParent().getParent().getParent()).isNull(); + then(sibling.getEnvironment().getProperty("custom.foo")).isEqualTo("sibling"); + then(sibling.getEnvironment().getProperty("spring.application.name")).isEqualTo("sibling"); } @Test @@ -537,17 +536,17 @@ public class BootstrapConfigurationTests { private void environmentEnrichedInParentContext(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); - then(this.context.getParent().getEnvironment()).isNotSameAs(this.context.getEnvironment()); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getParent().getEnvironment()).isNotSameAs(context.getEnvironment()); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); - then(((ConfigurableEnvironment) this.context.getParent().getEnvironment()).getPropertySources() + then(((ConfigurableEnvironment) context.getParent().getEnvironment()).getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -560,29 +559,29 @@ public class BootstrapConfigurationTests { .profiles("parent") .web(WebApplicationType.NONE) .run(); - this.context = new SpringApplicationBuilder(BareConfiguration.class) + context = new SpringApplicationBuilder(BareConfiguration.class) .properties("spring.config.use-legacy-processing=true") .profiles("child") .parent(parent) .web(WebApplicationType.NONE) .run(); - then(this.context.getParent().getEnvironment()).isNotSameAs(this.context.getEnvironment()); + then(context.getParent().getEnvironment()).isNotSameAs(context.getEnvironment()); // The ApplicationContext merges profiles (profiles and property sources), see // AbstractEnvironment.merge() - then(this.context.getEnvironment().acceptsProfiles("child", "parent")).isTrue(); + then(context.getEnvironment().acceptsProfiles("child", "parent")).isTrue(); // But the parent is not a child - then(this.context.getParent().getEnvironment().acceptsProfiles("child")).isFalse(); - then(this.context.getParent().getEnvironment().acceptsProfiles("parent")).isTrue(); - then(((ConfigurableEnvironment) this.context.getParent().getEnvironment()).getPropertySources() + then(context.getParent().getEnvironment().acceptsProfiles("child")).isFalse(); + then(context.getParent().getEnvironment().acceptsProfiles("parent")).isTrue(); + then(((ConfigurableEnvironment) context.getParent().getEnvironment()).getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); // The "bootstrap" property source is not shared now, but it has the same // properties in it because they are pulled from the PropertySourceConfiguration // below - then(this.context.getParent().getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getParent().getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); // The parent property source is there in the child because they are both in the // "parent" profile (by virtue of the merge in AbstractEnvironment) - then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("parent"); + then(context.getEnvironment().getProperty("info.name")).isEqualTo("parent"); } @Test @@ -598,37 +597,37 @@ public class BootstrapConfigurationTests { private void includeProfileFromBootstrapPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.include", "bar,baz"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz")).isTrue(); - then(this.context.getEnvironment().acceptsProfiles("bar")).isTrue(); + then(context.getEnvironment().acceptsProfiles("baz")).isTrue(); + then(context.getEnvironment().acceptsProfiles("bar")).isTrue(); } @Test public void activeProfileFromBootstrapPropertySource() { activeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true"); - then(this.context.getEnvironment().getActiveProfiles()).contains("foo"); + then(context.getEnvironment().getActiveProfiles()).contains("foo"); } @Test public void activeProfileFromBootstrapPropertySourceWithAppContext() { activeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true", "spring.cloud.config.initialize-on-context-refresh=true"); - then(this.context.getEnvironment().getActiveProfiles()).doesNotContain("after"); - then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo"); + then(context.getEnvironment().getActiveProfiles()).doesNotContain("after"); + then(context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo"); } private void activeProfileFromBootstrapPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.active", "bar,baz"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "foo")).isTrue(); + then(context.getEnvironment().acceptsProfiles("baz", "bar", "foo")).isTrue(); } @@ -646,13 +645,13 @@ public class BootstrapConfigurationTests { private void activeAndIncludeProfileFromBootstrapPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.active", "bar,baz"); PropertySourceConfiguration.MAP.put("spring.profiles.include", "bar,baz,hello"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); - then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); + then(context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); + then(context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); } @Test @@ -670,13 +669,13 @@ public class BootstrapConfigurationTests { private void activeAndIncludeProfileFromBootstrapPropertySourceWithReplacement(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.active", "${barreplacement},baz"); PropertySourceConfiguration.MAP.put("spring.profiles.include", "${barreplacement},baz,hello"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); - then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); + then(context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); + then(context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); } @Test @@ -692,12 +691,12 @@ public class BootstrapConfigurationTests { } private void includeProfileFromBootstrapProperties(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().acceptsProfiles("local")).isTrue(); - then(this.context.getEnvironment().getProperty("added")).isEqualTo("Hello added!"); + then(context.getEnvironment().acceptsProfiles("local")).isTrue(); + then(context.getEnvironment().getProperty("added")).isEqualTo("Hello added!"); } @Test @@ -713,28 +712,28 @@ public class BootstrapConfigurationTests { } private void nonEnumerablePropertySourceWorks(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().getProperty("foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("foo")).isEqualTo("bar"); } @Test void activeAndIncludeProfileFromBootstrapPropertySource_WhenMultiplePlacesHaveActiveProfileProperties_ShouldOnlyAcceptTheTopPriority() { String[] properties = new String[] { "spring.config.use-legacy-processing=true" }; - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run("--spring.profiles.active=prod,secure"); - then(this.context.getEnvironment().acceptsProfiles("prod", "secure")).isTrue(); + then(context.getEnvironment().acceptsProfiles("prod", "secure")).isTrue(); // active profile from property sources with lower priority should not be included - then(this.context.getEnvironment().acceptsProfiles("local")).isFalse(); - then(this.context.getEnvironment().getActiveProfiles()).contains("prod", "secure"); - then(this.context.getEnvironment().getActiveProfiles()).doesNotContain("local"); + then(context.getEnvironment().acceptsProfiles("local")).isFalse(); + then(context.getEnvironment().getActiveProfiles()).contains("prod", "secure"); + then(context.getEnvironment().getActiveProfiles()).doesNotContain("local"); // check if active profile value could possibly exist in other property sources // with lower priority - then(this.context.getEnvironment() + then(context.getEnvironment() .getPropertySources() .stream() .map(p -> p.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME)) @@ -805,10 +804,10 @@ public class BootstrapConfigurationTests { } } } - if (this.name != null) { - then(this.name).isEqualTo(environment.getProperty("spring.application.name")); + if (name != null) { + then(name).isEqualTo(environment.getProperty("spring.application.name")); } - if (this.fail) { + if (fail) { throw new RuntimeException("Planned"); } @@ -816,7 +815,7 @@ public class BootstrapConfigurationTests { } public String getName() { - return this.name; + return name; } public void setName(String name) { @@ -824,7 +823,7 @@ public class BootstrapConfigurationTests { } public boolean isFail() { - return this.fail; + return fail; } public void setFail(boolean fail) { @@ -854,10 +853,10 @@ public class BootstrapConfigurationTests { @Override public PropertySource locate(Environment environment) { - if (this.name != null) { - then(this.name).isEqualTo(environment.getProperty("spring.application.name")); + if (name != null) { + then(name).isEqualTo(environment.getProperty("spring.application.name")); } - if (this.fail) { + if (fail) { throw new RuntimeException("Planned"); } CompositePropertySource compositePropertySource = new CompositePropertySource("listTestBootstrap"); @@ -867,7 +866,7 @@ public class BootstrapConfigurationTests { } public String getName() { - return this.name; + return name; } public void setName(String name) { @@ -875,7 +874,7 @@ public class BootstrapConfigurationTests { } public boolean isFail() { - return this.fail; + return fail; } public void setFail(boolean fail) { diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java index e6ef9d39..468d7d5d 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java @@ -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. @@ -18,7 +18,6 @@ package org.springframework.cloud.bootstrap.encrypt; import java.nio.charset.StandardCharsets; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.cloud.context.encrypt.EncryptorFactory; @@ -26,10 +25,12 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.security.crypto.encrypt.TextEncryptor; import org.springframework.util.StreamUtils; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; import static org.assertj.core.api.BDDAssertions.then; /** * @author Biju Kunjummen + * @author Olga Maciaszek-Sharma */ public class EncryptorFactoryTests { @@ -51,7 +52,7 @@ public class EncryptorFactoryTests { -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5 """; - Assertions.assertThrows(RuntimeException.class, () -> new EncryptorFactory().create(key)); + assertThatRuntimeException().isThrownBy(() -> new EncryptorFactory().create(key)); } } diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java index 2516b1d7..ededcf34 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java @@ -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. @@ -19,7 +19,6 @@ package org.springframework.cloud.context.scope.refresh; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -41,6 +40,7 @@ import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.test.annotation.DirtiesContext; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.BDDAssertions.then; @SpringBootTest(classes = TestConfiguration.class) @@ -69,12 +69,12 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testSimpleProperties() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - then(this.service instanceof Advised).isTrue(); + then(service.getMessage()).isEqualTo("Hello scope!"); + then(service instanceof Advised).isTrue(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...but don't refresh, so the bean stays the same: - then(this.service.getMessage()).isEqualTo("Hello scope!"); + then(service.getMessage()).isEqualTo("Hello scope!"); then(ExampleService.getInitCount()).isEqualTo(0); then(ExampleService.getDestroyCount()).isEqualTo(0); } @@ -82,14 +82,14 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefresh() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refreshAll(); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refreshAll(); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -100,15 +100,15 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefreshBean() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refresh("service"); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refresh("service"); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -120,7 +120,7 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testCheckedException() { - Assertions.assertThrows(ServiceException.class, () -> this.service.throwsException()); + assertThatExceptionOfType(ServiceException.class).isThrownBy(() -> service.throwsException()); } public interface Service { @@ -166,28 +166,28 @@ public class RefreshScopeIntegrationTests { @Override public void afterPropertiesSet() throws Exception { - logger.debug("Initializing message: " + this.message); + logger.debug("Initializing message: " + message); initCount++; } @Override public void destroy() throws Exception { - logger.debug("Destroying message: " + this.message); + logger.debug("Destroying message: " + message); destroyCount++; - this.message = null; + message = null; } @Override public String getMessage() { - logger.debug("Getting message: " + this.message); + logger.debug("Getting message: " + message); try { - Thread.sleep(this.delay); + Thread.sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - logger.info("Returning message: " + this.message); - return this.message; + logger.info("Returning message: " + message); + return message; } public void setMessage(String message) { @@ -224,8 +224,8 @@ public class RefreshScopeIntegrationTests { @RefreshScope public ExampleService service() { ExampleService service = new ExampleService(); - service.setMessage(this.properties.getMessage()); - service.setDelay(this.properties.getDelay()); + service.setMessage(properties.getMessage()); + service.setDelay(properties.getDelay()); return service; } @@ -241,7 +241,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public String getMessage() { - return this.message; + return message; } public void setMessage(String message) { @@ -250,7 +250,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public int getDelay() { - return this.delay; + return delay; } public void setDelay(int delay) { diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java index 19a3c72c..efe1cdab 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * Copyright 2013-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. @@ -380,9 +380,10 @@ public final class ServiceInstanceListSupplierBuilder { return new HealthCheckServiceInstanceListSupplier(delegate, loadBalancerClientFactory, (serviceInstance, healthCheckPath) -> webClient.get() .uri(UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build().toUri()) - .exchange() - .flatMap(clientResponse -> clientResponse.releaseBody() - .thenReturn(HttpStatus.OK.equals(clientResponse.statusCode())))); + .retrieve() + .toBodilessEntity() + .map(response -> HttpStatus.OK.equals(response.getStatusCode())) + .onErrorReturn(false)); } private ServiceInstanceListSupplier blockingHealthCheckServiceInstanceListSupplier(RestTemplate restTemplate, diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java index 6899772b..183cd7d0 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java @@ -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. @@ -65,9 +65,9 @@ class XForwardedHeadersTransformerTests { HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).containsKey("X-Forwarded-Host"); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Host")).isTrue(); assertThat(newRequest.getHeaders().getFirst("X-Forwarded-Host")).isEqualTo("google.com"); - assertThat(newRequest.getHeaders()).containsKey("X-Forwarded-Proto"); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Proto")).isTrue(); assertThat(newRequest.getHeaders().getFirst("X-Forwarded-Proto")).isEqualTo("https"); } @@ -78,8 +78,8 @@ class XForwardedHeadersTransformerTests { XForwardedHeadersTransformer transformer = new XForwardedHeadersTransformer(loadBalancerClientFactory); HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).doesNotContainKey("X-Forwarded-Host"); - assertThat(newRequest.getHeaders()).doesNotContainKey("X-Forwarded-Proto"); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Host")).isFalse(); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Proto")).isFalse(); } } diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java index 1507fbf1..f5889529 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 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. @@ -703,7 +703,7 @@ class HealthCheckServiceInstanceListSupplierTests { } @Test - void shouldCheckUseProvidedPortForHealthCheckRequest() { + void shouldCheckUserProvidedPortForHealthCheckRequest() { Throwable exception = catchThrowable(() -> { String serviceId = "ignored-service"; properties.getHealthCheck().setPort(8888); @@ -718,7 +718,7 @@ class HealthCheckServiceInstanceListSupplierTests { listSupplier.isAlive(serviceInstance).block(); }); - assertThat(exception).hasMessageContaining("Connection refused: /127.0.0.1:888"); + assertThat(exception).hasMessageContaining("Connection refused: /127.0.0.1:8888"); } private static Stream healthCheckFunctions() { diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java index a7d8a376..a10f922d 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java @@ -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. @@ -74,7 +74,7 @@ class LoadBalancerServiceInstanceCookieTransformerTests { void shouldReturnPassedRequestWhenNoServiceInstance() { HttpRequest newRequest = transformer.transformRequest(request, null); - assertThat(newRequest.getHeaders()).doesNotContainKey(HttpHeaders.COOKIE); + assertThat(newRequest.getHeaders().containsHeader(HttpHeaders.COOKIE)).isFalse(); } @Test @@ -82,7 +82,7 @@ class LoadBalancerServiceInstanceCookieTransformerTests { properties.getStickySession().setInstanceIdCookieName(null); HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).doesNotContainKey(HttpHeaders.COOKIE); + assertThat(newRequest.getHeaders().containsHeader(HttpHeaders.COOKIE)).isFalse(); } @Test @@ -90,7 +90,7 @@ class LoadBalancerServiceInstanceCookieTransformerTests { properties.getStickySession().setInstanceIdCookieName(""); HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).doesNotContainKey(HttpHeaders.COOKIE); + assertThat(newRequest.getHeaders().containsHeader(HttpHeaders.COOKIE)).isFalse(); } diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java index ad2aee6e..21500ca7 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java @@ -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. @@ -17,6 +17,7 @@ package org.springframework.cloud.loadbalancer.core; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,8 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; import org.springframework.cloud.client.loadbalancer.Request; import org.springframework.cloud.client.loadbalancer.RequestData; import org.springframework.cloud.client.loadbalancer.RequestDataContext; -import org.springframework.http.HttpHeaders; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.ClientRequest; import static org.assertj.core.api.Assertions.assertThat; @@ -72,8 +74,8 @@ class RequestBasedStickySessionServiceInstanceListSupplierTests { @Test void shouldReturnInstanceBasedOnCookieFromClientRequest() { - HttpHeaders headers = new HttpHeaders(); - headers.add(properties.getStickySession().getInstanceIdCookieName(), "test-1"); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.put(properties.getStickySession().getInstanceIdCookieName(), Collections.singletonList("test-1")); when(clientRequest.cookies()).thenReturn(headers); Request request = new DefaultRequest<>( new RequestDataContext(new RequestData(clientRequest))); @@ -86,8 +88,8 @@ class RequestBasedStickySessionServiceInstanceListSupplierTests { @Test void shouldReturnAllDelegateInstancesIfInstanceBasedOnCookieFromClientRequestNotFound() { - HttpHeaders headers = new HttpHeaders(); - headers.add(properties.getStickySession().getInstanceIdCookieName(), "test-4"); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.put(properties.getStickySession().getInstanceIdCookieName(), Collections.singletonList("test-4")); when(clientRequest.cookies()).thenReturn(headers); Request request = new DefaultRequest<>( new RequestDataContext(new RequestData(clientRequest))); @@ -99,7 +101,7 @@ class RequestBasedStickySessionServiceInstanceListSupplierTests { @Test void shouldReturnAllInstancesFromDelegateIfClientRequestHasNoCookie() { - when(clientRequest.cookies()).thenReturn(new HttpHeaders()); + when(clientRequest.cookies()).thenReturn(new LinkedMultiValueMap<>()); Request request = new DefaultRequest<>( new RequestDataContext(new RequestData(clientRequest))); diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java index 7b12ca1a..45f526dc 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * Copyright 2013-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. @@ -28,7 +28,7 @@ import org.springframework.context.annotation.Import; import org.springframework.web.reactive.function.client.WebClient; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; +import static org.assertj.core.api.Fail.fail; import static org.mockito.Mockito.mock; public class ServiceInstanceListSupplierBuilderTests { diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java index 8dceeee7..068ab9f1 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * Copyright 2013-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. @@ -26,6 +26,7 @@ import org.springframework.http.HttpStatus; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; import org.springframework.web.util.UriComponentsBuilder; import static org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplierBuilder.getUri; @@ -46,9 +47,10 @@ final class ServiceInstanceListSuppliersTestUtils { static BiFunction> healthCheckFunction(WebClient webClient) { return (serviceInstance, healthCheckPath) -> webClient.get() .uri(UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build().toUri()) - .exchange() - .flatMap(clientResponse -> clientResponse.releaseBody() - .thenReturn(HttpStatus.OK.equals(clientResponse.statusCode()))); + .retrieve() + .toBodilessEntity() + .map(response -> HttpStatus.OK.equals(response.getStatusCode())) + .onErrorReturn(throwable -> !(throwable instanceof WebClientRequestException), false); } static BiFunction> healthCheckFunction(RestTemplate restTemplate) { diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java index 3c6b500c..12cf270d 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java @@ -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. @@ -66,9 +66,9 @@ class XForwardedHeadersTransformerTests { ClientRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.headers()).containsKey("X-Forwarded-Host"); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Host")).isTrue(); assertThat(newRequest.headers().getFirst("X-Forwarded-Host")).isEqualTo("spring.io"); - assertThat(newRequest.headers()).containsKey("X-Forwarded-Proto"); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Host")).isTrue(); assertThat(newRequest.headers().getFirst("X-Forwarded-Proto")).isEqualTo("https"); } @@ -79,8 +79,8 @@ class XForwardedHeadersTransformerTests { ClientRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.headers()).doesNotContainKey("X-Forwarded-Host"); - assertThat(newRequest.headers()).doesNotContainKey("X-Forwarded-Proto"); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Host")).isFalse(); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Proto")).isFalse(); } } diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java index c5d7bc5c..e6b14386 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java @@ -44,6 +44,7 @@ import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalanc import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMapAdapter; import static org.assertj.core.api.Assertions.assertThat; @@ -71,7 +72,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldRecordSuccessfulTimedRequest() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -93,6 +94,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { Tag.of("serviceInstance.port", "8080"), Tag.of("status", "200"), Tag.of("uri", "/test")); } + @SuppressWarnings("unchecked") @Test void shouldNotAddPathValueWhenDisabled() { ReactiveLoadBalancer.Factory factory = mock(ReactiveLoadBalancer.Factory.class); @@ -102,7 +104,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { MicrometerStatsLoadBalancerLifecycle statsLifecycle = new MicrometerStatsLoadBalancerLifecycle(meterRegistry, factory); RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -126,7 +128,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { String uriTemplate = "/test/{pathParam}/test"; attributes.put(attributeName, uriTemplate); RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test/123/test"), - new HttpHeaders(), new HttpHeaders(), attributes); + new HttpHeaders(), new LinkedMultiValueMap<>(), attributes); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -152,7 +154,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldRecordFailedTimedRequest() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -175,7 +177,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldNotRecordDiscardedRequest() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new EmptyResponse(); statsLifecycle.onStartRequest(lbRequest, lbResponse); @@ -247,7 +249,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldHandleNullLoadBalancerResponse() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); assertThatCode(() -> { statsLifecycle.onStartRequest(lbRequest, null);