mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Restore RANDOM_PORT handing in tests with a separate management port
This commit fixes a regression where RANDOM_PORT was no longer honored if a defined management port is set. Due to the modularization efforts, the code has moved from an EnvironmentPostProcessor to an ApplicationListener. Unfortunately, the listener is registered too late to handle the event it is listening to. While the event type could have been changed, the listener was added on the ApplicationContext which are not honored before the ApplicationContext is in a state to be used. The contract of ContextCustomizerFactory is already giving us everything we need. While the environment is post-processed later than we would like, it is still post-processed before the refresh state so that the additional property is honored. This commit also adds an integration test to cover this scenario. Closes gh-48653
This commit is contained in:
+16
-18
@@ -20,10 +20,8 @@ import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
@@ -33,6 +31,8 @@ import org.springframework.core.env.PropertyResolver;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.lang.Contract;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -45,25 +45,13 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SpringBootTestRandomPortApplicationListener implements SmartApplicationListener, Ordered {
|
||||
class SpringBootTestRandomPortContextCustomizer implements ContextCustomizer {
|
||||
|
||||
private static final String TEST_SOURCE_NAME = TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
|
||||
postProcessEnvironment(environmentPreparedEvent.getEnvironment());
|
||||
}
|
||||
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
postProcessEnvironment(context.getEnvironment());
|
||||
}
|
||||
|
||||
void postProcessEnvironment(ConfigurableEnvironment environment) {
|
||||
@@ -92,6 +80,16 @@ class SpringBootTestRandomPortApplicationListener implements SmartApplicationLis
|
||||
return (!managementPort.equals(serverPort)) ? "0" : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj != null) && (obj.getClass() == getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
private enum Port {
|
||||
|
||||
SERVER("server.port"), MANAGEMENT("management.server.port");
|
||||
+3
-24
@@ -20,15 +20,13 @@ import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} apply
|
||||
* {@link SpringBootTestRandomPortApplicationListener} to tests.
|
||||
* {@link ContextCustomizerFactory} implementation to apply
|
||||
* {@link SpringBootTestRandomPortContextCustomizer} to tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@@ -37,26 +35,7 @@ class SpringBootTestRandomPortContextCustomizerFactory implements ContextCustomi
|
||||
@Override
|
||||
public @Nullable ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
return new Customizer();
|
||||
}
|
||||
|
||||
static class Customizer implements ContextCustomizer {
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
context.addApplicationListener(new SpringBootTestRandomPortApplicationListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj != null) && (obj.getClass() == getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
return new SpringBootTestRandomPortContextCustomizer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-17
@@ -34,14 +34,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTestRandomPortApplicationListener}.
|
||||
* Tests for {@link SpringBootTestRandomPortContextCustomizer}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SpringBootTestRandomPortApplicationListenerTests {
|
||||
class SpringBootTestRandomPortContextCustomizerTests {
|
||||
|
||||
private final SpringBootTestRandomPortApplicationListener listener = new SpringBootTestRandomPortApplicationListener();
|
||||
private final SpringBootTestRandomPortContextCustomizer customizer = new SpringBootTestRandomPortContextCustomizer();
|
||||
|
||||
private MockEnvironment environment;
|
||||
|
||||
@@ -56,7 +56,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
@Test
|
||||
void postProcessWhenServerAndManagementPortIsZeroInTestPropertySource() {
|
||||
addTestPropertySource("0", "0");
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("management.server.port", "0");
|
||||
this.propertySources.addLast(new MapPropertySource("other", source));
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
@@ -77,7 +77,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
addTestPropertySource("8080", "8081");
|
||||
this.environment.setProperty("server.port", "8080");
|
||||
this.environment.setProperty("management.server.port", "8081");
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("8080");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("8081");
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndTestManagementPortIsNotNull() {
|
||||
addTestPropertySource("0", "8080");
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("8080");
|
||||
}
|
||||
@@ -93,7 +93,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndManagementPortIsNull() {
|
||||
addTestPropertySource("0", null);
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isNull();
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
other.put("management.server.port", "8081");
|
||||
MapPropertySource otherSource = new MapPropertySource("other", other);
|
||||
this.propertySources.addLast(otherSource);
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEmpty();
|
||||
}
|
||||
@@ -118,7 +118,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "8080")));
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEmpty();
|
||||
}
|
||||
@@ -128,7 +128,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "8081")));
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
@@ -138,7 +138,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "-1")));
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("-1");
|
||||
}
|
||||
@@ -148,7 +148,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", 8081)));
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
@@ -162,7 +162,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
testPropertySource.getSource().put("port", "9090");
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "${port}")));
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
@@ -173,7 +173,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "${port}")));
|
||||
assertThatExceptionOfType(PlaceholderResolutionException.class)
|
||||
.isThrownBy(() -> this.listener.postProcessEnvironment(this.environment))
|
||||
.isThrownBy(() -> this.customizer.postProcessEnvironment(this.environment))
|
||||
.withMessage("Could not resolve placeholder 'port' in value \"${port}\"");
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
source.put("server.port", "${port}");
|
||||
source.put("management.server.port", "9090");
|
||||
this.propertySources.addLast(new MapPropertySource("other", source));
|
||||
this.listener.postProcessEnvironment(this.environment);
|
||||
this.customizer.postProcessEnvironment(this.environment);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
@@ -201,7 +201,7 @@ class SpringBootTestRandomPortApplicationListenerTests {
|
||||
source.put("management.server.port", "9090");
|
||||
this.propertySources.addLast(new MapPropertySource("other", source));
|
||||
assertThatExceptionOfType(PlaceholderResolutionException.class)
|
||||
.isThrownBy(() -> this.listener.postProcessEnvironment(this.environment))
|
||||
.isThrownBy(() -> this.customizer.postProcessEnvironment(this.environment))
|
||||
.withMessage("Could not resolve placeholder 'port' in value \"${port}\"");
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-present 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 smoketest.actuator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.resttestclient.TestRestTemplate;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalManagementPort;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for separate management and main service ports when a management port
|
||||
* is set.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, args = "--management.server.port=1234")
|
||||
class ManagementRandomPortWithConfiguredPortSampleActuatorApplicationTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int serverPort;
|
||||
|
||||
@LocalManagementPort
|
||||
private int managementPort;
|
||||
|
||||
@Test
|
||||
void randomPortsShouldBeDifferent() {
|
||||
assertThat(this.serverPort).isPositive();
|
||||
assertThat(this.managementPort).isPositive();
|
||||
assertThat(this.serverPort).isNotEqualTo(this.managementPort);
|
||||
}
|
||||
|
||||
@Test
|
||||
void randomManagementPortShouldBeApplied() {
|
||||
assertThat(this.managementPort).isNotEqualTo(1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHealth() {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().withBasicAuth("user", "password")
|
||||
.getForEntity("http://localhost:" + this.managementPort + "/actuator/health", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user