sink) {
+ try {
+ Health health = this.delegate.health();
+ sink.success(health);
+ }
+ catch (Exception ex) {
+ sink.error(ex);
+ }
+ }
+
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthIndicator.java
new file mode 100644
index 0000000000..455f2bfd41
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthIndicator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.health;
+
+import reactor.core.publisher.Mono;
+
+/**
+ * Defines the {@link Health} of an arbitrary system or component.
+ *
+ * This is non blocking contract that is meant to be used in a reactive application. See
+ * {@link HealthIndicator} for the traditional contract.
+ *
+ * @author Stephane Nicoll
+ * @since 2.0.0
+ * @see HealthIndicator
+ */
+@FunctionalInterface
+public interface ReactiveHealthIndicator {
+
+ /**
+ * Provide the indicator of health.
+ * @return a {@link Mono} that provides the {@link Health}
+ */
+ Mono health();
+
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisHealthIndicator.java
index 96e4a6838c..85f5c0c700 100644
--- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisHealthIndicator.java
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisHealthIndicator.java
@@ -35,9 +35,9 @@ import org.springframework.util.Assert;
*/
public class RedisHealthIndicator extends AbstractHealthIndicator {
- private static final String VERSION = "version";
+ static final String VERSION = "version";
- private static final String REDIS_VERSION = "redis_version";
+ static final String REDIS_VERSION = "redis_version";
private final RedisConnectionFactory redisConnectionFactory;
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisReactiveHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisReactiveHealthIndicator.java
new file mode 100644
index 0000000000..6b44f96ec7
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisReactiveHealthIndicator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.health;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
+
+/**
+ * A {@link ReactiveHealthIndicator} for Redis.
+ *
+ * @author Stephane Nicoll
+ * @since 2.0.0
+ */
+public class RedisReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
+
+ private final ReactiveRedisConnectionFactory connectionFactory;
+
+ public RedisReactiveHealthIndicator(
+ ReactiveRedisConnectionFactory connectionFactory) {
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ protected Mono doHealthCheck(Health.Builder builder) {
+ return this.connectionFactory.getReactiveConnection().serverCommands().info()
+ .map(info -> builder.up().withDetail(
+ RedisHealthIndicator.VERSION, info.getProperty(
+ RedisHealthIndicator.REDIS_VERSION)).build());
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java
new file mode 100644
index 0000000000..357bc11367
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfigurationTests.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.autoconfigure.endpoint;
+
+import org.junit.Test;
+import reactor.core.publisher.Mono;
+
+import org.springframework.boot.actuate.endpoint.HealthEndpoint;
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.boot.actuate.health.HealthIndicator;
+import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
+import org.springframework.boot.actuate.health.Status;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for {@link EndpointAutoConfiguration}.
+ *
+ * @author Stephane Nicoll
+ */
+public class EndpointAutoConfigurationTests {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class));
+
+ @Test
+ public void healthEndpointAdaptReactiveHealthIndicator() {
+ this.contextRunner.withUserConfiguration(
+ ReactiveHealthIndicatorConfiguration.class).run((context) -> {
+ ReactiveHealthIndicator reactiveHealthIndicator = context.getBean(
+ "reactiveHealthIndicator", ReactiveHealthIndicator.class);
+ verify(reactiveHealthIndicator, times(0)).health();
+ Health health = context.getBean(HealthEndpoint.class).health();
+ assertThat(health.getStatus()).isEqualTo(Status.UP);
+ assertThat(health.getDetails()).containsOnlyKeys("reactive");
+ verify(reactiveHealthIndicator, times(1)).health();
+ });
+ }
+
+ @Test
+ public void healthEndpointMergeRegularAndReactive() {
+ this.contextRunner.withUserConfiguration(HealthIndicatorConfiguration.class,
+ ReactiveHealthIndicatorConfiguration.class).run((context) -> {
+ HealthIndicator simpleHealthIndicator = context.getBean(
+ "simpleHealthIndicator", HealthIndicator.class);
+ ReactiveHealthIndicator reactiveHealthIndicator = context.getBean(
+ "reactiveHealthIndicator", ReactiveHealthIndicator.class);
+ verify(simpleHealthIndicator, times(0)).health();
+ verify(reactiveHealthIndicator, times(0)).health();
+ Health health = context.getBean(HealthEndpoint.class).health();
+ assertThat(health.getStatus()).isEqualTo(Status.UP);
+ assertThat(health.getDetails()).containsOnlyKeys("simple", "reactive");
+ verify(simpleHealthIndicator, times(1)).health();
+ verify(reactiveHealthIndicator, times(1)).health();
+ });
+ }
+
+
+ @Configuration
+ static class HealthIndicatorConfiguration {
+
+ @Bean
+ public HealthIndicator simpleHealthIndicator() {
+ HealthIndicator mock = mock(HealthIndicator.class);
+ given(mock.health()).willReturn(Health.status(Status.UP).build());
+ return mock;
+ }
+
+ }
+
+ @Configuration
+ static class ReactiveHealthIndicatorConfiguration {
+
+ @Bean
+ public ReactiveHealthIndicator reactiveHealthIndicator() {
+ ReactiveHealthIndicator mock = mock(ReactiveHealthIndicator.class);
+ given(mock.health()).willReturn(Mono.just(Health.status(Status.UP).build()));
+ return mock;
+ }
+
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java
index 87c2360e84..33d02b82b3 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointManagementContextConfigurationTests.java
@@ -25,14 +25,17 @@ import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
import org.springframework.boot.actuate.endpoint.web.AuditEventsWebEndpointExtension;
+import org.springframework.boot.actuate.endpoint.web.HealthReactiveWebEndpointExtension;
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
import org.springframework.boot.actuate.endpoint.web.HeapDumpWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.LogFileWebEndpoint;
+import org.springframework.boot.actuate.endpoint.web.StatusReactiveWebEndpointExtension;
import org.springframework.boot.actuate.endpoint.web.StatusWebEndpointExtension;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.util.ReflectionTestUtils;
@@ -67,7 +70,7 @@ public class WebEndpointManagementContextConfigurationTests {
@Test
public void healthStatusMappingCanBeCustomized() {
- ApplicationContextRunner contextRunner = contextRunner()
+ WebApplicationContextRunner contextRunner = webContextRunner()
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
.withUserConfiguration(HealthEndpointConfiguration.class);
contextRunner.run((context) -> {
@@ -94,7 +97,7 @@ public class WebEndpointManagementContextConfigurationTests {
@Test
public void statusMappingCanBeCustomized() {
- ApplicationContextRunner contextRunner = contextRunner()
+ WebApplicationContextRunner contextRunner = webContextRunner()
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
.withUserConfiguration(StatusEndpointConfiguration.class);
contextRunner.run((context) -> {
@@ -113,6 +116,70 @@ public class WebEndpointManagementContextConfigurationTests {
"status", StatusEndpointConfiguration.class);
}
+ @Test
+ public void reactiveHealthWebEndpointExtensionIsAutoConfigured() {
+ reactiveWebContextRunner(HealthEndpointConfiguration.class).run((context) -> {
+ assertThat(context).hasSingleBean(HealthReactiveWebEndpointExtension.class);
+ assertThat(context).doesNotHaveBean(HealthWebEndpointExtension.class);
+ });
+
+ }
+
+ @Test
+ public void reactiveHealthStatusMappingCanBeCustomized() {
+ reactiveWebContextRunner(HealthEndpointConfiguration.class)
+ .withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
+ .run((context) -> {
+ HealthReactiveWebEndpointExtension extension = context
+ .getBean(HealthReactiveWebEndpointExtension.class);
+ Map statusMappings = getStatusMapping(extension);
+ assertThat(statusMappings).containsEntry("DOWN", 503);
+ assertThat(statusMappings).containsEntry("OUT_OF_SERVICE", 503);
+ assertThat(statusMappings).containsEntry("CUSTOM", 500);
+ });
+ }
+
+ @Test
+ public void reactiveHealthWebEndpointExtensionCanBeDisabled() {
+ reactiveWebContextRunner(HealthEndpointConfiguration.class)
+ .withPropertyValues("endpoints.health.enabled=false").run((context) -> {
+ assertThat(context).doesNotHaveBean(HealthReactiveWebEndpointExtension.class);
+ assertThat(context).doesNotHaveBean(HealthWebEndpointExtension.class);
+ });
+
+ }
+
+ @Test
+ public void reactiveStatusWebEndpointExtensionIsAutoConfigured() {
+ reactiveWebContextRunner(StatusEndpointConfiguration.class).run((context) -> {
+ assertThat(context).hasSingleBean(StatusReactiveWebEndpointExtension.class);
+ assertThat(context).doesNotHaveBean(StatusWebEndpointExtension.class);
+ });
+ }
+
+ @Test
+ public void reactiveStatusMappingCanBeCustomized() {
+ reactiveWebContextRunner(StatusEndpointConfiguration.class)
+ .withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
+ .run((context) -> {
+ StatusReactiveWebEndpointExtension extension = context
+ .getBean(StatusReactiveWebEndpointExtension.class);
+ Map statusMappings = getStatusMapping(extension);
+ assertThat(statusMappings).containsEntry("DOWN", 503);
+ assertThat(statusMappings).containsEntry("OUT_OF_SERVICE", 503);
+ assertThat(statusMappings).containsEntry("CUSTOM", 500);
+ });
+ }
+
+ @Test
+ public void reactiveStatusWebEndpointExtensionCanBeDisabled() {
+ reactiveWebContextRunner(StatusEndpointConfiguration.class)
+ .withPropertyValues("endpoints.status.enabled=false").run((context) -> {
+ assertThat(context).doesNotHaveBean(StatusReactiveWebEndpointExtension.class);
+ assertThat(context).doesNotHaveBean(StatusWebEndpointExtension.class);
+ });
+ }
+
@Test
public void auditEventsWebEndpointExtensionIsAutoConfigured() {
beanIsAutoConfigured(AuditEventsWebEndpointExtension.class,
@@ -128,28 +195,28 @@ public class WebEndpointManagementContextConfigurationTests {
@Test
public void logFileWebEndpointIsAutoConfiguredWhenLoggingFileIsSet() {
- contextRunner().withPropertyValues("logging.file:test.log").run(
+ webContextRunner().withPropertyValues("logging.file:test.log").run(
(context) -> assertThat(context.getBeansOfType(LogFileWebEndpoint.class))
.hasSize(1));
}
@Test
public void logFileWebEndpointIsAutoConfiguredWhenLoggingPathIsSet() {
- contextRunner().withPropertyValues("logging.path:test/logs").run(
+ webContextRunner().withPropertyValues("logging.path:test/logs").run(
(context) -> assertThat(context.getBeansOfType(LogFileWebEndpoint.class))
.hasSize(1));
}
@Test
public void logFileWebEndpointIsAutoConfiguredWhenExternalFileIsSet() {
- contextRunner().withPropertyValues("endpoints.logfile.external-file:external.log")
+ webContextRunner().withPropertyValues("endpoints.logfile.external-file:external.log")
.run((context) -> assertThat(
context.getBeansOfType(LogFileWebEndpoint.class)).hasSize(1));
}
@Test
public void logFileWebEndpointCanBeDisabled() {
- contextRunner()
+ webContextRunner()
.withPropertyValues("logging.file:test.log",
"endpoints.logfile.enabled:false")
.run((context) -> assertThat(context)
@@ -157,19 +224,31 @@ public class WebEndpointManagementContextConfigurationTests {
}
private void beanIsAutoConfigured(Class> beanType, Class>... config) {
- contextRunner().withPropertyValues("endpoints.default.web.enabled:true")
+ webContextRunner().withPropertyValues("endpoints.default.web.enabled:true")
.withUserConfiguration(config)
.run((context) -> assertThat(context).hasSingleBean(beanType));
}
+ private ReactiveWebApplicationContextRunner reactiveWebContextRunner(
+ Class>... config) {
+ return reactiveWebContextRunner()
+ .withPropertyValues("endpoints.default.web.enabled:true")
+ .withUserConfiguration(config);
+ }
+
private void beanIsNotAutoConfiguredWhenEndpointIsDisabled(Class> webExtension,
String id, Class>... config) {
- contextRunner().withPropertyValues("endpoints." + id + ".enabled=false")
+ webContextRunner().withPropertyValues("endpoints." + id + ".enabled=false")
.run((context) -> assertThat(context).doesNotHaveBean(webExtension));
}
- private ApplicationContextRunner contextRunner() {
- return new ApplicationContextRunner().withConfiguration(
+ private WebApplicationContextRunner webContextRunner() {
+ return new WebApplicationContextRunner().withConfiguration(
+ AutoConfigurations.of(WebEndpointManagementContextConfiguration.class));
+ }
+
+ private ReactiveWebApplicationContextRunner reactiveWebContextRunner() {
+ return new ReactiveWebApplicationContextRunner().withConfiguration(
AutoConfigurations.of(WebEndpointManagementContextConfiguration.class));
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorAutoConfigurationTests.java
index 29ed402976..05bef5714e 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorAutoConfigurationTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorAutoConfigurationTests.java
@@ -16,501 +16,37 @@
package org.springframework.boot.actuate.autoconfigure.health;
-import java.util.Map;
-
-import javax.sql.DataSource;
-
-import io.searchbox.client.JestClient;
-import org.assertj.core.api.Condition;
import org.junit.Test;
-import org.neo4j.ogm.session.SessionFactory;
-import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
-import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
-import org.springframework.boot.actuate.health.CassandraHealthIndicator;
-import org.springframework.boot.actuate.health.CompositeHealthIndicator;
-import org.springframework.boot.actuate.health.CouchbaseHealthIndicator;
-import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
-import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
-import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
-import org.springframework.boot.actuate.health.ElasticsearchJestHealthIndicator;
-import org.springframework.boot.actuate.health.Health;
-import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.boot.actuate.health.JmsHealthIndicator;
-import org.springframework.boot.actuate.health.LdapHealthIndicator;
-import org.springframework.boot.actuate.health.MailHealthIndicator;
-import org.springframework.boot.actuate.health.MongoHealthIndicator;
-import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
-import org.springframework.boot.actuate.health.RabbitHealthIndicator;
-import org.springframework.boot.actuate.health.RedisHealthIndicator;
-import org.springframework.boot.actuate.health.SolrHealthIndicator;
+import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
+import org.springframework.boot.actuate.health.RedisReactiveHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
-import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
-import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
-import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
-import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
-import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
-import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.boot.test.context.runner.ContextConsumer;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.cassandra.core.CassandraOperations;
-import org.springframework.data.couchbase.core.CouchbaseOperations;
-import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
-import org.springframework.ldap.core.LdapOperations;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
/**
- * Tests for {@link HealthIndicatorAutoConfiguration}.
+ * Tests for {@link HealthIndicatorAutoConfiguration} that validates the outcome of
+ * combining reactive and non reactive health indicators.
*
- * @author Christian Dupuis
* @author Stephane Nicoll
- * @author Andy Wilkinson
- * @author Eddú Meléndez
- * @author Eric Spiegelberg
*/
public class HealthIndicatorAutoConfigurationTests {
public final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
- AutoConfigurations.of(HealthIndicatorAutoConfiguration.class,
- ManagementServerProperties.class));
+ AutoConfigurations.of(HealthIndicatorAutoConfiguration.class));
@Test
- public void defaultHealthIndicator() {
- this.contextRunner.withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void defaultHealthIndicatorsDisabled() {
- this.contextRunner.withPropertyValues("management.health.defaults.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void defaultHealthIndicatorsDisabledWithCustomOne() {
- this.contextRunner.withUserConfiguration(CustomHealthIndicator.class)
- .withPropertyValues("management.health.defaults.enabled:false")
- .run((context) -> {
- Map beans = context
- .getBeansOfType(HealthIndicator.class);
- assertThat(beans).hasSize(1);
- assertThat(context.getBean("customHealthIndicator"))
- .isSameAs(beans.values().iterator().next());
- });
- }
-
- @Test
- public void defaultHealthIndicatorsDisabledButOne() {
- this.contextRunner
- .withPropertyValues("management.health.defaults.enabled:false",
- "management.health.diskspace.enabled:true")
- .run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
- }
-
- @Test
- public void redisHealthIndicator() {
+ public void reactiveRedisTakePrecedence() {
this.contextRunner
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(RedisHealthIndicator.class));
- }
-
- @Test
- public void notRedisHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
- .withPropertyValues("management.health.redis.enabled:false",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void mongoHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
- MongoDataAutoConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(MongoHealthIndicator.class));
- }
-
- @Test
- public void notMongoHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
- MongoDataAutoConfiguration.class))
- .withPropertyValues("management.health.mongo.enabled:false",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void combinedHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
- RedisAutoConfiguration.class, MongoDataAutoConfiguration.class,
- SolrAutoConfiguration.class))
.run((context) -> {
- Map beans = context
- .getBeansOfType(HealthIndicator.class);
- assertThat(beans).hasSize(4);
+ assertThat(context).hasSingleBean(ReactiveHealthIndicator.class);
+ assertThat(context).getBean("redisHealthIndicator")
+ .isInstanceOf(RedisReactiveHealthIndicator.class);
});
}
- @Test
- public void dataSourceHealthIndicator() {
- this.contextRunner
- .withConfiguration(
- AutoConfigurations.of(DataSourceAutoConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
- }
-
- @Test
- public void dataSourceHealthIndicatorWithSeveralDataSources() {
- this.contextRunner
- .withUserConfiguration(EmbeddedDataSourceConfiguration.class,
- DataSourceConfig.class)
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run((context) -> {
- Map beans = context
- .getBeansOfType(HealthIndicator.class);
- assertThat(beans).hasSize(1);
- HealthIndicator bean = beans.values().iterator().next();
- assertThat(bean).isExactlyInstanceOf(CompositeHealthIndicator.class);
- assertThat(bean.health().getDetails()).containsOnlyKeys("dataSource",
- "testDataSource");
- });
- }
-
- @Test
- public void dataSourceHealthIndicatorWithAbstractRoutingDataSource() {
- this.contextRunner
- .withUserConfiguration(EmbeddedDataSourceConfiguration.class,
- RoutingDatasourceConfig.class)
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
- }
-
- @Test
- public void dataSourceHealthIndicatorWithCustomValidationQuery() {
- this.contextRunner
- .withUserConfiguration(DataSourceConfig.class,
- DataSourcePoolMetadataProvidersConfiguration.class,
- HealthIndicatorAutoConfiguration.class)
- .withPropertyValues(
- "spring.datasource.test.validation-query:SELECT from FOOBAR",
- "management.health.diskspace.enabled:false")
- .run((context) -> {
- Map beans = context
- .getBeansOfType(HealthIndicator.class);
- assertThat(beans).hasSize(1);
- HealthIndicator healthIndicator = beans.values().iterator().next();
- assertThat(healthIndicator.getClass())
- .isEqualTo(DataSourceHealthIndicator.class);
- DataSourceHealthIndicator dataSourceHealthIndicator = (DataSourceHealthIndicator) healthIndicator;
- assertThat(dataSourceHealthIndicator.getQuery())
- .isEqualTo("SELECT from FOOBAR");
- });
- }
-
- @Test
- public void notDataSourceHealthIndicator() {
- this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
- .withPropertyValues("management.health.db.enabled:false",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void rabbitHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(RabbitHealthIndicator.class));
- }
-
- @Test
- public void notRabbitHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
- .withPropertyValues("management.health.rabbit.enabled:false",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void solrHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(SolrHealthIndicator.class));
- }
-
- @Test
- public void notSolrHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
- .withPropertyValues("management.health.solr.enabled:false",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void diskSpaceHealthIndicator() {
- this.contextRunner.run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
- }
-
- @Test
- public void mailHealthIndicator() {
- this.contextRunner
- .withConfiguration(
- AutoConfigurations.of(MailSenderAutoConfiguration.class))
- .withPropertyValues("spring.mail.host:smtp.acme.org",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(MailHealthIndicator.class));
- }
-
- @Test
- public void notMailHealthIndicator() {
- this.contextRunner
- .withConfiguration(
- AutoConfigurations.of(MailSenderAutoConfiguration.class))
- .withPropertyValues("spring.mail.host:smtp.acme.org",
- "management.health.mail.enabled:false",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void jmsHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(JmsHealthIndicator.class));
- }
-
- @Test
- public void notJmsHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
- .withPropertyValues("management.health.jms.enabled:false",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void elasticsearchHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
- JestAutoConfiguration.class,
- ElasticsearchAutoConfiguration.class))
- .withPropertyValues("spring.data.elasticsearch.cluster-nodes:localhost:0",
- "management.health.diskspace.enabled:false")
- .withSystemProperties("es.set.netty.runtime.available.processors=false")
- .run(hasSingleHealthIndicator(ElasticsearchHealthIndicator.class));
- }
-
- @Test
- public void elasticsearchJestHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
- JestAutoConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .withSystemProperties("es.set.netty.runtime.available.processors=false")
- .run(hasSingleHealthIndicator(ElasticsearchJestHealthIndicator.class));
- }
-
- @Test
- public void notElasticsearchHealthIndicator() {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
- JestAutoConfiguration.class,
- ElasticsearchAutoConfiguration.class))
- .withPropertyValues("management.health.elasticsearch.enabled:false",
- "spring.data.elasticsearch.properties.path.home:target",
- "management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void cassandraHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(CassandraHealthIndicator.class));
- }
-
- @Test
- public void notCassandraHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false",
- "management.health.cassandra.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void couchbaseHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(CouchbaseHealthIndicator.class));
- }
-
- @Test
- public void notCouchbaseHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false",
- "management.health.couchbase.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void ldapHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(LdapHealthIndicator.class));
- }
-
- @Test
- public void notLdapHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false",
- "management.health.ldap.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- @Test
- public void neo4jHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false")
- .run(hasSingleHealthIndicator(Neo4jHealthIndicator.class));
- }
-
- @Test
- public void notNeo4jHealthIndicator() throws Exception {
- this.contextRunner
- .withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
- .withPropertyValues("management.health.diskspace.enabled:false",
- "management.health.neo4j.enabled:false")
- .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
- }
-
- private ContextConsumer hasSingleHealthIndicator(
- Class extends HealthIndicator> type) {
- return (context) -> assertThat(context).getBeans(HealthIndicator.class).hasSize(1)
- .hasValueSatisfying(
- new Condition<>((indicator) -> indicator.getClass().equals(type),
- "Wrong indicator type"));
- }
-
- @Configuration
- @EnableConfigurationProperties
- protected static class DataSourceConfig {
-
- @Bean
- @ConfigurationProperties(prefix = "spring.datasource.test")
- public DataSource testDataSource() {
- return DataSourceBuilder.create()
- .type(org.apache.tomcat.jdbc.pool.DataSource.class)
- .driverClassName("org.hsqldb.jdbc.JDBCDriver")
- .url("jdbc:hsqldb:mem:test").username("sa").build();
- }
-
- }
-
- @Configuration
- protected static class RoutingDatasourceConfig {
-
- @Bean
- AbstractRoutingDataSource routingDataSource() {
- return mock(AbstractRoutingDataSource.class);
- }
-
- }
-
- @Configuration
- protected static class CustomHealthIndicator {
-
- @Bean
- public HealthIndicator customHealthIndicator() {
- return () -> Health.down().build();
- }
-
- }
-
- @Configuration
- @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
- protected static class CassandraConfiguration {
-
- @Bean
- public CassandraOperations cassandraOperations() {
- return mock(CassandraOperations.class);
- }
-
- }
-
- @Configuration
- @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
- protected static class CouchbaseConfiguration {
-
- @Bean
- public CouchbaseOperations couchbaseOperations() {
- return mock(CouchbaseOperations.class);
- }
-
- }
-
- @Configuration
- protected static class JestClientConfiguration {
-
- @Bean
- public JestClient jestClient() {
- return mock(JestClient.class);
- }
-
- }
-
- @Configuration
- @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
- protected static class LdapConfiguration {
-
- @Bean
- public LdapOperations ldapOperations() {
- return mock(LdapOperations.class);
- }
-
- }
-
- @Configuration
- @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
- protected static class Neo4jConfiguration {
-
- @Bean
- public SessionFactory sessionFactory() {
- return mock(SessionFactory.class);
- }
-
- }
-
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorsConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorsConfigurationTests.java
new file mode 100644
index 0000000000..c5b396f02e
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorsConfigurationTests.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.autoconfigure.health;
+
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import io.searchbox.client.JestClient;
+import org.assertj.core.api.Condition;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.neo4j.ogm.session.SessionFactory;
+
+import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
+import org.springframework.boot.actuate.health.CassandraHealthIndicator;
+import org.springframework.boot.actuate.health.CompositeHealthIndicator;
+import org.springframework.boot.actuate.health.CouchbaseHealthIndicator;
+import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
+import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
+import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
+import org.springframework.boot.actuate.health.ElasticsearchJestHealthIndicator;
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.boot.actuate.health.HealthIndicator;
+import org.springframework.boot.actuate.health.JmsHealthIndicator;
+import org.springframework.boot.actuate.health.LdapHealthIndicator;
+import org.springframework.boot.actuate.health.MailHealthIndicator;
+import org.springframework.boot.actuate.health.MongoHealthIndicator;
+import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
+import org.springframework.boot.actuate.health.RabbitHealthIndicator;
+import org.springframework.boot.actuate.health.RedisHealthIndicator;
+import org.springframework.boot.actuate.health.SolrHealthIndicator;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
+import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
+import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
+import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
+import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
+import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.context.runner.ContextConsumer;
+import org.springframework.boot.testsupport.runner.classpath.ClassPathExclusions;
+import org.springframework.boot.testsupport.runner.classpath.ModifiedClassPathRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.cassandra.core.CassandraOperations;
+import org.springframework.data.couchbase.core.CouchbaseOperations;
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+import org.springframework.ldap.core.LdapOperations;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests for {@link HealthIndicatorsConfiguration}.
+ *
+ * @author Christian Dupuis
+ * @author Stephane Nicoll
+ * @author Andy Wilkinson
+ * @author Eddú Meléndez
+ * @author Eric Spiegelberg
+ */
+@RunWith(ModifiedClassPathRunner.class)
+@ClassPathExclusions({ "reactor-core*.jar", "lettuce-core*.jar" })
+public class HealthIndicatorsConfigurationTests {
+
+ public final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(
+ AutoConfigurations.of(HealthIndicatorAutoConfiguration.class));
+
+ @Test
+ public void defaultHealthIndicator() {
+ this.contextRunner.withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void defaultHealthIndicatorsDisabled() {
+ this.contextRunner.withPropertyValues("management.health.defaults.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void defaultHealthIndicatorsDisabledWithCustomOne() {
+ this.contextRunner.withUserConfiguration(CustomHealthIndicator.class)
+ .withPropertyValues("management.health.defaults.enabled:false")
+ .run((context) -> {
+ Map beans = context
+ .getBeansOfType(HealthIndicator.class);
+ assertThat(beans).hasSize(1);
+ assertThat(context.getBean("customHealthIndicator"))
+ .isSameAs(beans.values().iterator().next());
+ });
+ }
+
+ @Test
+ public void defaultHealthIndicatorsDisabledButOne() {
+ this.contextRunner
+ .withPropertyValues("management.health.defaults.enabled:false",
+ "management.health.diskspace.enabled:true")
+ .run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
+ }
+
+ @Test
+ public void redisHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(RedisHealthIndicator.class));
+ }
+
+ @Test
+ public void notRedisHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
+ .withPropertyValues("management.health.redis.enabled:false",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void mongoHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
+ MongoDataAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(MongoHealthIndicator.class));
+ }
+
+ @Test
+ public void notMongoHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
+ MongoDataAutoConfiguration.class))
+ .withPropertyValues("management.health.mongo.enabled:false",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void combinedHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
+ RedisAutoConfiguration.class, MongoDataAutoConfiguration.class,
+ SolrAutoConfiguration.class))
+ .run((context) -> {
+ Map beans = context
+ .getBeansOfType(HealthIndicator.class);
+ assertThat(beans).hasSize(4);
+ });
+ }
+
+ @Test
+ public void dataSourceHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(
+ AutoConfigurations.of(DataSourceAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
+ }
+
+ @Test
+ public void dataSourceHealthIndicatorWithSeveralDataSources() {
+ this.contextRunner
+ .withUserConfiguration(EmbeddedDataSourceConfiguration.class,
+ DataSourceConfig.class)
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run((context) -> {
+ Map beans = context
+ .getBeansOfType(HealthIndicator.class);
+ assertThat(beans).hasSize(1);
+ HealthIndicator bean = beans.values().iterator().next();
+ assertThat(bean).isExactlyInstanceOf(CompositeHealthIndicator.class);
+ assertThat(bean.health().getDetails()).containsOnlyKeys("dataSource",
+ "testDataSource");
+ });
+ }
+
+ @Test
+ public void dataSourceHealthIndicatorWithAbstractRoutingDataSource() {
+ this.contextRunner
+ .withUserConfiguration(EmbeddedDataSourceConfiguration.class,
+ RoutingDatasourceConfig.class)
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
+ }
+
+ @Test
+ public void dataSourceHealthIndicatorWithCustomValidationQuery() {
+ this.contextRunner
+ .withUserConfiguration(DataSourceConfig.class,
+ DataSourcePoolMetadataProvidersConfiguration.class,
+ HealthIndicatorAutoConfiguration.class)
+ .withPropertyValues(
+ "spring.datasource.test.validation-query:SELECT from FOOBAR",
+ "management.health.diskspace.enabled:false")
+ .run((context) -> {
+ Map beans = context
+ .getBeansOfType(HealthIndicator.class);
+ assertThat(beans).hasSize(1);
+ HealthIndicator healthIndicator = beans.values().iterator().next();
+ assertThat(healthIndicator.getClass())
+ .isEqualTo(DataSourceHealthIndicator.class);
+ DataSourceHealthIndicator dataSourceHealthIndicator = (DataSourceHealthIndicator) healthIndicator;
+ assertThat(dataSourceHealthIndicator.getQuery())
+ .isEqualTo("SELECT from FOOBAR");
+ });
+ }
+
+ @Test
+ public void notDataSourceHealthIndicator() {
+ this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
+ .withPropertyValues("management.health.db.enabled:false",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void rabbitHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(RabbitHealthIndicator.class));
+ }
+
+ @Test
+ public void notRabbitHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
+ .withPropertyValues("management.health.rabbit.enabled:false",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void solrHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(SolrHealthIndicator.class));
+ }
+
+ @Test
+ public void notSolrHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
+ .withPropertyValues("management.health.solr.enabled:false",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void diskSpaceHealthIndicator() {
+ this.contextRunner.run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
+ }
+
+ @Test
+ public void mailHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(
+ AutoConfigurations.of(MailSenderAutoConfiguration.class))
+ .withPropertyValues("spring.mail.host:smtp.acme.org",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(MailHealthIndicator.class));
+ }
+
+ @Test
+ public void notMailHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(
+ AutoConfigurations.of(MailSenderAutoConfiguration.class))
+ .withPropertyValues("spring.mail.host:smtp.acme.org",
+ "management.health.mail.enabled:false",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void jmsHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(JmsHealthIndicator.class));
+ }
+
+ @Test
+ public void notJmsHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
+ .withPropertyValues("management.health.jms.enabled:false",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void elasticsearchHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
+ JestAutoConfiguration.class,
+ ElasticsearchAutoConfiguration.class))
+ .withPropertyValues("spring.data.elasticsearch.cluster-nodes:localhost:0",
+ "management.health.diskspace.enabled:false")
+ .withSystemProperties("es.set.netty.runtime.available.processors=false")
+ .run(hasSingleHealthIndicator(ElasticsearchHealthIndicator.class));
+ }
+
+ @Test
+ public void elasticsearchJestHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
+ JestAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .withSystemProperties("es.set.netty.runtime.available.processors=false")
+ .run(hasSingleHealthIndicator(ElasticsearchJestHealthIndicator.class));
+ }
+
+ @Test
+ public void notElasticsearchHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
+ JestAutoConfiguration.class,
+ ElasticsearchAutoConfiguration.class))
+ .withPropertyValues("management.health.elasticsearch.enabled:false",
+ "spring.data.elasticsearch.properties.path.home:target",
+ "management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void cassandraHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(CassandraHealthIndicator.class));
+ }
+
+ @Test
+ public void notCassandraHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false",
+ "management.health.cassandra.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void couchbaseHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(CouchbaseHealthIndicator.class));
+ }
+
+ @Test
+ public void notCouchbaseHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false",
+ "management.health.couchbase.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void ldapHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(LdapHealthIndicator.class));
+ }
+
+ @Test
+ public void notLdapHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false",
+ "management.health.ldap.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ @Test
+ public void neo4jHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleHealthIndicator(Neo4jHealthIndicator.class));
+ }
+
+ @Test
+ public void notNeo4jHealthIndicator() throws Exception {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false",
+ "management.health.neo4j.enabled:false")
+ .run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
+ }
+
+ private ContextConsumer hasSingleHealthIndicator(
+ Class extends HealthIndicator> type) {
+ return (context) -> assertThat(context).getBeans(HealthIndicator.class).hasSize(1)
+ .hasValueSatisfying(
+ new Condition<>((indicator) -> indicator.getClass().equals(type),
+ "Wrong indicator type"));
+ }
+
+ @Configuration
+ @EnableConfigurationProperties
+ protected static class DataSourceConfig {
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource.test")
+ public DataSource testDataSource() {
+ return DataSourceBuilder.create()
+ .type(org.apache.tomcat.jdbc.pool.DataSource.class)
+ .driverClassName("org.hsqldb.jdbc.JDBCDriver")
+ .url("jdbc:hsqldb:mem:test").username("sa").build();
+ }
+
+ }
+
+ @Configuration
+ protected static class RoutingDatasourceConfig {
+
+ @Bean
+ AbstractRoutingDataSource routingDataSource() {
+ return mock(AbstractRoutingDataSource.class);
+ }
+
+ }
+
+ @Configuration
+ protected static class CustomHealthIndicator {
+
+ @Bean
+ public HealthIndicator customHealthIndicator() {
+ return () -> Health.down().build();
+ }
+
+ }
+
+ @Configuration
+ @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
+ protected static class CassandraConfiguration {
+
+ @Bean
+ public CassandraOperations cassandraOperations() {
+ return mock(CassandraOperations.class);
+ }
+
+ }
+
+ @Configuration
+ @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
+ protected static class CouchbaseConfiguration {
+
+ @Bean
+ public CouchbaseOperations couchbaseOperations() {
+ return mock(CouchbaseOperations.class);
+ }
+
+ }
+
+ @Configuration
+ protected static class JestClientConfiguration {
+
+ @Bean
+ public JestClient jestClient() {
+ return mock(JestClient.class);
+ }
+
+ }
+
+ @Configuration
+ @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
+ protected static class LdapConfiguration {
+
+ @Bean
+ public LdapOperations ldapOperations() {
+ return mock(LdapOperations.class);
+ }
+
+ }
+
+ @Configuration
+ @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
+ protected static class Neo4jConfiguration {
+
+ @Bean
+ public SessionFactory sessionFactory() {
+ return mock(SessionFactory.class);
+ }
+
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/ReactiveHealthIndicatorsConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/ReactiveHealthIndicatorsConfigurationTests.java
new file mode 100644
index 0000000000..d25c969f57
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/health/ReactiveHealthIndicatorsConfigurationTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.autoconfigure.health;
+
+import org.assertj.core.api.Condition;
+import org.junit.Test;
+
+import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
+import org.springframework.boot.actuate.health.RedisReactiveHealthIndicator;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.context.runner.ContextConsumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ReactiveHealthIndicatorsConfiguration}.
+ *
+ * @author Stephane Nicoll
+ */
+public class ReactiveHealthIndicatorsConfigurationTests {
+
+ public final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(
+ AutoConfigurations.of(HealthIndicatorAutoConfiguration.class));
+
+ @Test
+ public void redisHealthIndicator() {
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(
+ RedisAutoConfiguration.class))
+ .withPropertyValues("management.health.diskspace.enabled:false")
+ .run(hasSingleReactiveHealthIndicator(RedisReactiveHealthIndicator.class));
+ }
+
+ private ContextConsumer hasSingleReactiveHealthIndicator(
+ Class extends ReactiveHealthIndicator> type) {
+ return (context) -> assertThat(context).getBeans(ReactiveHealthIndicator.class)
+ .hasSize(1)
+ .hasValueSatisfying(
+ new Condition<>((indicator) -> indicator.getClass().equals(type),
+ "Wrong indicator type"));
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java
index eed17404af..a14c76ea24 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java
@@ -21,9 +21,9 @@ import java.util.Map;
import org.junit.Test;
+import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.boot.actuate.health.HealthIndicatorFactory;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.Status;
@@ -59,7 +59,7 @@ public class HealthEndpointTests {
private HealthIndicator createHealthIndicator(
Map healthIndicators) {
- return new HealthIndicatorFactory()
+ return new CompositeHealthIndicatorFactory()
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/StatusEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/StatusEndpointTests.java
index 625bcdde5f..0f49987465 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/StatusEndpointTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/StatusEndpointTests.java
@@ -21,9 +21,9 @@ import java.util.Map;
import org.junit.Test;
+import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.boot.actuate.health.HealthIndicatorFactory;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.Status;
@@ -52,7 +52,7 @@ public class StatusEndpointTests {
private HealthIndicator createHealthIndicator(
Map healthIndicators) {
- return new HealthIndicatorFactory()
+ return new CompositeHealthIndicatorFactory()
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/HealthEndpointWebIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/HealthEndpointWebIntegrationTests.java
index a01903c69f..38d0841b33 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/HealthEndpointWebIntegrationTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/HealthEndpointWebIntegrationTests.java
@@ -22,9 +22,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
+import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.boot.actuate.health.HealthIndicatorFactory;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.context.ConfigurableApplicationContext;
@@ -69,7 +69,7 @@ public class HealthEndpointWebIntegrationTests {
@Bean
public HealthEndpoint healthEndpoint(
Map healthIndicators) {
- return new HealthEndpoint(new HealthIndicatorFactory().createHealthIndicator(
+ return new HealthEndpoint(new CompositeHealthIndicatorFactory().createHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/StatusEndpointWebIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/StatusEndpointWebIntegrationTests.java
index 9ae5145354..7881eb33b8 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/StatusEndpointWebIntegrationTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/StatusEndpointWebIntegrationTests.java
@@ -22,9 +22,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
+import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.boot.actuate.health.HealthIndicatorFactory;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.context.ConfigurableApplicationContext;
@@ -67,7 +67,7 @@ public class StatusEndpointWebIntegrationTests {
@Bean
public StatusEndpoint statusEndpoint(
Map healthIndicators) {
- return new StatusEndpoint(new HealthIndicatorFactory().createHealthIndicator(
+ return new StatusEndpoint(new CompositeHealthIndicatorFactory().createHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthIndicatorFactoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorFactoryTests.java
similarity index 93%
rename from spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthIndicatorFactoryTests.java
rename to spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorFactoryTests.java
index d6a7170db5..5f04057a4a 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthIndicatorFactoryTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorFactoryTests.java
@@ -24,13 +24,13 @@ import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
- * Tests for {@link HealthIndicatorFactory}.
+ * Tests for {@link CompositeHealthIndicatorFactory}.
*
* @author Phillip Webb
* @author Christian Dupuis
* @author Andy Wilkinson
*/
-public class HealthIndicatorFactoryTests {
+public class CompositeHealthIndicatorFactoryTests {
@Test
public void upAndUpIsAggregatedToUp() throws Exception {
@@ -62,7 +62,7 @@ public class HealthIndicatorFactoryTests {
private HealthIndicator createHealthIndicator(
Map healthIndicators) {
- return new HealthIndicatorFactory()
+ return new CompositeHealthIndicatorFactory()
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeReactiveHealthIndicatorFactoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeReactiveHealthIndicatorFactoryTests.java
new file mode 100644
index 0000000000..2ea4e763c6
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeReactiveHealthIndicatorFactoryTests.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.health;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.Test;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for {@link CompositeReactiveHealthIndicatorFactory}.
+ *
+ * @author Stephane Nicoll
+ */
+public class CompositeReactiveHealthIndicatorFactoryTests {
+
+ private static final Health UP = new Health.Builder().status(Status.UP).build();
+
+ private static final Health DOWN = new Health.Builder().status(Status.DOWN).build();
+
+ @Test
+ public void noHealthIndicator() {
+ ReactiveHealthIndicator healthIndicator = createHealthIndicator(
+ Collections.singletonMap("test", () -> Mono.just(UP)), null);
+ StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).containsOnlyKeys("test");
+ }).verifyComplete();
+ }
+
+ @Test
+ public void defaultHealthIndicatorNameFactory() {
+ ReactiveHealthIndicator healthIndicator = new CompositeReactiveHealthIndicatorFactory()
+ .createReactiveHealthIndicator(new OrderedHealthAggregator(),
+ Collections.singletonMap("myHealthIndicator", () -> Mono.just(UP)),
+ null);
+ StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).containsOnlyKeys("my");
+ }).verifyComplete();
+ }
+
+ @Test
+ public void healthIndicatorIsAdapted() {
+ ReactiveHealthIndicator healthIndicator = createHealthIndicator(
+ Collections.singletonMap("test", () -> Mono.just(UP)),
+ Collections.singletonMap("regular", () -> DOWN));
+ StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.DOWN);
+ assertThat(h.getDetails()).containsOnlyKeys("test", "regular");
+ }).verifyComplete();
+ }
+
+ @Test
+ public void reactiveHealthIndicatorTakesPrecedence() {
+ ReactiveHealthIndicator reactivehealthIndicator = mock(ReactiveHealthIndicator.class);
+ given(reactivehealthIndicator.health()).willReturn(Mono.just(UP));
+ HealthIndicator regularHealthIndicator = mock(HealthIndicator.class);
+ given(regularHealthIndicator.health()).willReturn(UP);
+ ReactiveHealthIndicator healthIndicator = createHealthIndicator(
+ Collections.singletonMap("test", reactivehealthIndicator),
+ Collections.singletonMap("test", regularHealthIndicator));
+ StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).containsOnlyKeys("test");
+ }).verifyComplete();
+ verify(reactivehealthIndicator, times(1)).health();
+ verify(regularHealthIndicator, times(0)).health();
+ }
+
+ private ReactiveHealthIndicator createHealthIndicator(
+ Map reactiveHealthIndicators,
+ Map healthIndicators) {
+ return new CompositeReactiveHealthIndicatorFactory(n -> n)
+ .createReactiveHealthIndicator(new OrderedHealthAggregator(),
+ reactiveHealthIndicators, healthIndicators);
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeReactiveHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeReactiveHealthIndicatorTests.java
new file mode 100644
index 0000000000..5fac3aff58
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeReactiveHealthIndicatorTests.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.health;
+
+import java.time.Duration;
+
+import org.junit.Test;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link CompositeReactiveHealthIndicator}.
+ *
+ * @author Stephane Nicoll
+ */
+public class CompositeReactiveHealthIndicatorTests {
+
+ private static final Health UNKNOWN_HEALTH = Health.unknown()
+ .withDetail("detail", "value").build();
+
+ private static final Health HEALTHY = Health.up().build();
+
+ private OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
+
+ private CompositeReactiveHealthIndicator indicator =
+ new CompositeReactiveHealthIndicator(this.healthAggregator);
+
+ @Test
+ public void singleIndicator() {
+ this.indicator.addHealthIndicator("test", () -> Mono.just(HEALTHY));
+ StepVerifier.create(this.indicator.health()).consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).containsOnlyKeys("test");
+ assertThat(h.getDetails().get("test")).isEqualTo(HEALTHY);
+ }).verifyComplete();
+ }
+
+ @Test
+ public void longHealth() {
+ for (int i = 0; i < 50; i++) {
+ this.indicator.addHealthIndicator(
+ "test" + i, new TimeoutHealth(10000, Status.UP));
+ }
+ StepVerifier.withVirtualTime(this.indicator::health)
+ .expectSubscription()
+ .thenAwait(Duration.ofMillis(10000))
+ .consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).hasSize(50);
+ })
+ .verifyComplete();
+
+ }
+
+ @Test
+ public void timeoutReachedUsesFallback() {
+ this.indicator.addHealthIndicator("slow", new TimeoutHealth(10000, Status.UP))
+ .addHealthIndicator("fast", new TimeoutHealth(10, Status.UP))
+ .timeoutStrategy(100, UNKNOWN_HEALTH);
+ StepVerifier.create(this.indicator.health()).consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).containsOnlyKeys("slow", "fast");
+ assertThat(h.getDetails().get("slow")).isEqualTo(UNKNOWN_HEALTH);
+ assertThat(h.getDetails().get("fast")).isEqualTo(HEALTHY);
+ }).verifyComplete();
+ }
+
+ @Test
+ public void timeoutNotReached() {
+ this.indicator.addHealthIndicator("slow", new TimeoutHealth(10000, Status.UP))
+ .addHealthIndicator("fast", new TimeoutHealth(10, Status.UP))
+ .timeoutStrategy(20000, null);
+ StepVerifier.withVirtualTime(this.indicator::health)
+ .expectSubscription()
+ .thenAwait(Duration.ofMillis(10000))
+ .consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).containsOnlyKeys("slow", "fast");
+ assertThat(h.getDetails().get("slow")).isEqualTo(HEALTHY);
+ assertThat(h.getDetails().get("fast")).isEqualTo(HEALTHY);
+ })
+ .verifyComplete();
+ }
+
+ static class TimeoutHealth implements ReactiveHealthIndicator {
+
+ private final long timeout;
+
+ private final Status status;
+
+ TimeoutHealth(long timeout, Status status) {
+ this.timeout = timeout;
+ this.status = status;
+ }
+
+ @Override
+ public Mono health() {
+ return Mono.delay(Duration.ofMillis(this.timeout))
+ .map(l -> Health.status(this.status).build());
+ }
+
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthIndicatorReactiveAdapterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthIndicatorReactiveAdapterTests.java
new file mode 100644
index 0000000000..59ba21ddf9
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthIndicatorReactiveAdapterTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.health;
+
+import org.junit.Test;
+import reactor.test.StepVerifier;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests for {@link HealthIndicatorReactiveAdapter}.
+ *
+ * @author Stephane Nicoll
+ */
+public class HealthIndicatorReactiveAdapterTests {
+
+ @Test
+ public void delegateReturnsHealth() {
+ HealthIndicator delegate = mock(HealthIndicator.class);
+ HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
+ Health status = Health.up().build();
+ given(delegate.health()).willReturn(status);
+ StepVerifier.create(adapter.health()).expectNext(status).verifyComplete();
+ }
+
+ @Test
+ public void delegateThrowError() {
+ HealthIndicator delegate = mock(HealthIndicator.class);
+ HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
+ given(delegate.health()).willThrow(new IllegalStateException("Expected"));
+ StepVerifier.create(adapter.health()).expectError(IllegalStateException.class);
+ }
+
+ @Test
+ public void delegateRunsOnTheElasticScheduler() {
+ String currentThread = Thread.currentThread().getName();
+ HealthIndicator delegate = () -> Health.status(Thread.currentThread().getName()
+ .equals(currentThread) ? Status.DOWN : Status.UP).build();
+ HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
+ StepVerifier.create(adapter.health()).expectNext(Health.status(Status.UP).build())
+ .verifyComplete();
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java
index 40d56eaab7..ed40299df3 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java
@@ -22,11 +22,6 @@ import java.util.Properties;
import org.junit.Test;
-import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
-import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.connection.ClusterInfo;
import org.springframework.data.redis.connection.RedisClusterConnection;
@@ -49,18 +44,6 @@ import static org.mockito.Mockito.verify;
*/
public class RedisHealthIndicatorTests {
- @Test
- public void indicatorExists() {
- new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class,
- EndpointAutoConfiguration.class,
- HealthIndicatorAutoConfiguration.class))
- .run((context) -> {
- assertThat(context).hasSingleBean(RedisConnectionFactory.class);
- assertThat(context).hasSingleBean(RedisHealthIndicator.class);
- });
- }
-
@Test
public void redisIsUp() throws Exception {
Properties info = new Properties();
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisReactiveHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisReactiveHealthIndicatorTests.java
new file mode 100644
index 0000000000..79f0f970a7
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisReactiveHealthIndicatorTests.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012-2017 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
+ *
+ * http://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.boot.actuate.health;
+
+import java.util.Properties;
+
+import org.junit.Test;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import org.springframework.data.redis.RedisConnectionFailureException;
+import org.springframework.data.redis.connection.ReactiveRedisConnection;
+import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
+import org.springframework.data.redis.connection.ReactiveServerCommands;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests for {@link RedisReactiveHealthIndicator}.
+ *
+ * @author Stephane Nicoll
+ */
+public class RedisReactiveHealthIndicatorTests {
+
+ @Test
+ public void redisIsUp() throws Exception {
+ Properties info = new Properties();
+ info.put("redis_version", "2.8.9");
+ ReactiveServerCommands commands = mock(ReactiveServerCommands.class);
+ given(commands.info()).willReturn(Mono.just(info));
+ RedisReactiveHealthIndicator healthIndicator = createHealthIndicator(commands);
+ Mono health = healthIndicator.health();
+ StepVerifier.create(health).consumeNextWith(h -> {
+ assertThat(h.getStatus()).isEqualTo(Status.UP);
+ assertThat(h.getDetails()).containsOnlyKeys("version");
+ assertThat(h.getDetails().get("version")).isEqualTo("2.8.9");
+ }).verifyComplete();
+ }
+
+ @Test
+ public void redisIsDown() throws Exception {
+ ReactiveServerCommands commands = mock(ReactiveServerCommands.class);
+ given(commands.info()).willReturn(Mono.error(
+ new RedisConnectionFailureException("Connection failed")));
+ RedisReactiveHealthIndicator healthIndicator = createHealthIndicator(commands);
+ Mono health = healthIndicator.health();
+ StepVerifier.create(health)
+ .expectError(RedisConnectionFailureException.class);
+ }
+
+ private RedisReactiveHealthIndicator createHealthIndicator(
+ ReactiveServerCommands serverCommands) {
+ ReactiveRedisConnection redisConnection = mock(ReactiveRedisConnection.class);
+ ReactiveRedisConnectionFactory redisConnectionFactory = mock(
+ ReactiveRedisConnectionFactory.class);
+ given(redisConnectionFactory.getReactiveConnection()).willReturn(redisConnection);
+ given(redisConnection.serverCommands()).willReturn(serverCommands);
+ return new RedisReactiveHealthIndicator(redisConnectionFactory);
+ }
+
+}