diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java index 252aaea3742b..5512da403deb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java @@ -16,53 +16,38 @@ package org.springframework.boot.actuate.autoconfigure.couchbase; -import java.util.Map; - import com.couchbase.client.java.Bucket; -import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthIndicatorConfiguration; import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; -import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration; -import org.springframework.context.annotation.Bean; +import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration; import org.springframework.context.annotation.Configuration; -import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.context.annotation.Import; /** * {@link EnableAutoConfiguration Auto-configuration} for * {@link CouchbaseHealthIndicator}. * * @author EddĂș MelĂ©ndez + * @author Mikalai Lushchytski * @since 2.0.0 */ @Configuration -@ConditionalOnClass({ CouchbaseOperations.class, Bucket.class }) -@ConditionalOnBean(CouchbaseOperations.class) +@ConditionalOnClass(Bucket.class) @ConditionalOnEnabledHealthIndicator("couchbase") @AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) -@AutoConfigureAfter(CouchbaseDataAutoConfiguration.class) -public class CouchbaseHealthIndicatorAutoConfiguration extends - CompositeHealthIndicatorConfiguration { - - private final Map couchbaseOperations; - - public CouchbaseHealthIndicatorAutoConfiguration( - Map couchbaseOperations) { - this.couchbaseOperations = couchbaseOperations; - } - - @Bean - @ConditionalOnMissingBean(name = "couchbaseHealthIndicator") - public HealthIndicator couchbaseHealthIndicator() { - return createHealthIndicator(this.couchbaseOperations); - } +@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, + CouchbaseDataAutoConfiguration.class, + CouchbaseReactiveDataAutoConfiguration.class }) +@Import({ CouchbaseHealthIndicatorConfiguration.class, + CouchbaseReactiveHealthIndicatorConfiguration.class }) +public class CouchbaseHealthIndicatorAutoConfiguration { } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java new file mode 100644 index 000000000000..5ac3cb79e2b6 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfiguration.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-2018 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.couchbase; + +import java.util.Map; + +import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthIndicatorConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.core.CouchbaseOperations; + +/** + * Configuration for {@link CouchbaseHealthIndicator}. + * + * @author Mikalai Lushchytski + * @since 2.1.0 + */ +@Configuration +@ConditionalOnClass(CouchbaseOperations.class) +@ConditionalOnBean(CouchbaseOperations.class) +public class CouchbaseHealthIndicatorConfiguration extends + CompositeHealthIndicatorConfiguration { + + private final Map couchbaseOperations; + + CouchbaseHealthIndicatorConfiguration( + Map couchbaseOperations) { + this.couchbaseOperations = couchbaseOperations; + } + + @Bean + @ConditionalOnMissingBean(name = "couchbaseHealthIndicator") + public HealthIndicator couchbaseHealthIndicator() { + return createHealthIndicator(this.couchbaseOperations); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java new file mode 100644 index 000000000000..9a85bb87c1bb --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfiguration.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2018 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.couchbase; + +import java.util.Map; + +import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthIndicatorConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; + +/** + * Configuration for + * {@link org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator}. + * + * @author Mikalai Lushchytski + * @since 2.1.0 + */ +@Configuration +@ConditionalOnClass(RxJavaCouchbaseOperations.class) +@ConditionalOnBean(RxJavaCouchbaseOperations.class) +public class CouchbaseReactiveHealthIndicatorConfiguration extends + CompositeReactiveHealthIndicatorConfiguration { + + private final Map couchbaseOperations; + + CouchbaseReactiveHealthIndicatorConfiguration( + Map couchbaseOperations) { + this.couchbaseOperations = couchbaseOperations; + } + + @Bean + @ConditionalOnMissingBean(name = "couchbaseReactiveHealthIndicator") + public ReactiveHealthIndicator couchbaseReactiveHealthIndicator() { + return createHealthIndicator(this.couchbaseOperations); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java new file mode 100644 index 000000000000..c90a6bd20fe5 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorConfigurationTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2018 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.couchbase; + +import org.junit.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; +import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ApplicationHealthIndicator; +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 org.springframework.data.couchbase.core.CouchbaseOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CouchbaseHealthIndicatorConfiguration}. + * + * @author Mikalai Lushchytski + */ +public class CouchbaseHealthIndicatorConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withUserConfiguration(CouchbaseMockConfiguration.class).withConfiguration( + AutoConfigurations.of(CouchbaseHealthIndicatorAutoConfiguration.class, + HealthIndicatorAutoConfiguration.class)); + + @Test + public void runShouldCreateIndicator() { + this.contextRunner.run((context) -> assertThat(context) + .hasSingleBean(CouchbaseHealthIndicator.class) + .doesNotHaveBean(CouchbaseReactiveHealthIndicator.class) + .doesNotHaveBean(ApplicationHealthIndicator.class)); + } + + @Test + public void runWhenDisabledShouldNotCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(CouchbaseHealthIndicator.class) + .hasSingleBean(ApplicationHealthIndicator.class)); + } + + @Configuration + protected static class CouchbaseMockConfiguration { + + @Bean + public CouchbaseOperations couchbaseOperations() { + return mock(CouchbaseOperations.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java new file mode 100644 index 000000000000..04b1d61ab853 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseReactiveHealthIndicatorConfigurationTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2018 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.couchbase; + +import org.junit.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator; +import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ApplicationHealthIndicator; +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 org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CouchbaseReactiveHealthIndicatorConfiguration}. + * + * @author Mikalai Lushchytski + */ +public class CouchbaseReactiveHealthIndicatorConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withUserConfiguration(CouchbaseMockConfiguration.class).withConfiguration( + AutoConfigurations.of(CouchbaseHealthIndicatorAutoConfiguration.class, + HealthIndicatorAutoConfiguration.class)); + + @Test + public void runShouldCreateIndicator() { + this.contextRunner.run((context) -> assertThat(context) + .hasSingleBean(CouchbaseReactiveHealthIndicator.class) + .doesNotHaveBean(CouchbaseHealthIndicator.class) + .doesNotHaveBean(ApplicationHealthIndicator.class)); + } + + @Test + public void runWhenDisabledShouldNotCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false") + .run((context) -> assertThat(context) + .doesNotHaveBean(CouchbaseReactiveHealthIndicator.class) + .hasSingleBean(ApplicationHealthIndicator.class)); + } + + @Configuration + protected static class CouchbaseMockConfiguration { + + @Bean + public RxJavaCouchbaseOperations couchbaseOperations() { + return mock(RxJavaCouchbaseOperations.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator/pom.xml b/spring-boot-project/spring-boot-actuator/pom.xml index 055feabaf396..cb8d8806df80 100644 --- a/spring-boot-project/spring-boot-actuator/pom.xml +++ b/spring-boot-project/spring-boot-actuator/pom.xml @@ -77,6 +77,11 @@ + + io.reactivex + rxjava-reactive-streams + true + javax.cache cache-api diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java new file mode 100644 index 000000000000..9f43994c0aff --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2018 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.couchbase; + +import com.couchbase.client.java.bucket.BucketInfo; +import com.couchbase.client.java.cluster.ClusterInfo; +import reactor.core.publisher.Mono; +import rx.Observable; +import rx.RxReactiveStreams; +import rx.Single; + +import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; +import org.springframework.util.StringUtils; + +/** + * A {@link org.springframework.boot.actuate.health.ReactiveHealthIndicator} for + * Couchbase. + * + * @author Mikalai Lushchytski + * @since 2.1.0 + */ +public class CouchbaseReactiveHealthIndicator extends AbstractReactiveHealthIndicator { + + private final RxJavaCouchbaseOperations couchbaseOperations; + + /** + * Create a new {@link CouchbaseReactiveHealthIndicator} instance. + * @param couchbaseOperations Reactive couchbase client. + */ + public CouchbaseReactiveHealthIndicator( + RxJavaCouchbaseOperations couchbaseOperations) { + this.couchbaseOperations = couchbaseOperations; + } + + @Override + protected Mono doHealthCheck(Health.Builder builder) { + ClusterInfo cluster = this.couchbaseOperations.getCouchbaseClusterInfo(); + String versions = StringUtils + .collectionToCommaDelimitedString(cluster.getAllVersions()); + Observable bucket = this.couchbaseOperations.getCouchbaseBucket() + .bucketManager().async().info(); + Single health = bucket.map(BucketInfo::nodeList) + .map(StringUtils::collectionToCommaDelimitedString) + .map((nodes) -> up(builder, versions, nodes)) + .onErrorReturn((error) -> down(builder, error)).toSingle(); + return Mono.from(RxReactiveStreams.toPublisher(health)); + } + + private Health up(Health.Builder builder, String versions, String nodes) { + return builder.up().withDetail("versions", versions).withDetail("nodes", nodes) + .build(); + } + + private Health down(Health.Builder builder, Throwable error) { + return builder.down(error).build(); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java new file mode 100644 index 000000000000..f728a42f62ce --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012-2018 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.couchbase; + +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collections; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.bucket.AsyncBucketManager; +import com.couchbase.client.java.bucket.BucketInfo; +import com.couchbase.client.java.bucket.BucketManager; +import com.couchbase.client.java.cluster.ClusterInfo; +import com.couchbase.client.java.error.TranscodingException; +import com.couchbase.client.java.util.features.Version; +import org.junit.Test; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import rx.Observable; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.data.couchbase.core.RxJavaCouchbaseOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CouchbaseReactiveHealthIndicator}. + */ +public class CouchbaseReactiveHealthIndicatorTests { + + @Test + public void testCouchbaseIsUp() { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + RxJavaCouchbaseOperations rxJavaCouchbaseOperations = mock( + RxJavaCouchbaseOperations.class); + given(rxJavaCouchbaseOperations.getCouchbaseClusterInfo()) + .willReturn(clusterInfo); + given(clusterInfo.getAllVersions()) + .willReturn(Arrays.asList(new Version(5, 5, 0), new Version(6, 0, 0))); + Bucket bucket = mock(Bucket.class); + BucketManager bucketManager = mock(BucketManager.class); + AsyncBucketManager asyncBucketManager = mock(AsyncBucketManager.class); + given(rxJavaCouchbaseOperations.getCouchbaseBucket()).willReturn(bucket); + given(bucket.bucketManager()).willReturn(bucketManager); + given(bucketManager.async()).willReturn(asyncBucketManager); + BucketInfo info = mock(BucketInfo.class); + given(asyncBucketManager.info()).willReturn(Observable.just(info)); + + InetAddress node1Address = mock(InetAddress.class); + InetAddress node2Address = mock(InetAddress.class); + given(info.nodeList()).willReturn(Arrays.asList(node1Address, node2Address)); + given(node1Address.toString()).willReturn("127.0.0.1"); + given(node2Address.toString()).willReturn("127.0.0.2"); + + CouchbaseReactiveHealthIndicator couchbaseReactiveHealthIndicator = new CouchbaseReactiveHealthIndicator( + rxJavaCouchbaseOperations); + + Mono health = couchbaseReactiveHealthIndicator.health(); + StepVerifier.create(health).consumeNextWith((h) -> { + assertThat(h.getStatus()).isEqualTo(Status.UP); + assertThat(h.getDetails()).containsKeys("versions", "nodes"); + assertThat(h.getDetails().get("versions")).isEqualTo("5.5.0,6.0.0"); + assertThat(h.getDetails().get("nodes")).isEqualTo("127.0.0.1,127.0.0.2"); + }).verifyComplete(); + } + + @Test + public void testCouchbaseIsDown() { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + RxJavaCouchbaseOperations rxJavaCouchbaseOperations = mock( + RxJavaCouchbaseOperations.class); + given(rxJavaCouchbaseOperations.getCouchbaseClusterInfo()) + .willReturn(clusterInfo); + given(clusterInfo.getAllVersions()) + .willReturn(Collections.singletonList(new Version(5, 5, 0))); + BucketManager bucketManager = mock(BucketManager.class); + AsyncBucketManager asyncBucketManager = mock(AsyncBucketManager.class); + Bucket bucket = mock(Bucket.class); + given(rxJavaCouchbaseOperations.getCouchbaseBucket()).willReturn(bucket); + given(bucket.bucketManager()).willReturn(bucketManager); + given(bucketManager.async()).willReturn(asyncBucketManager); + given(asyncBucketManager.info()) + .willReturn(Observable.error(new TranscodingException("Failure"))); + CouchbaseReactiveHealthIndicator couchbaseReactiveHealthIndicator = new CouchbaseReactiveHealthIndicator( + rxJavaCouchbaseOperations); + + Mono health = couchbaseReactiveHealthIndicator.health(); + StepVerifier.create(health).consumeNextWith((h) -> { + assertThat(h.getStatus()).isEqualTo(Status.DOWN); + assertThat(h.getDetails()).containsOnlyKeys("error"); + assertThat(h.getDetails().get("error")) + .isEqualTo(TranscodingException.class.getName() + ": Failure"); + }).verifyComplete(); + } + +}