diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle index c85920eebf8f..c8b5621a8c4c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle @@ -32,6 +32,7 @@ dependencies { optional(platform(project(":spring-boot-project:spring-boot-dependencies"))) optional("ch.qos.logback:logback-classic") + optional("com.datastax.oss:java-driver-core") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") optional("com.github.ben-manes.caffeine:caffeine") optional("com.hazelcast:hazelcast") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverHealthContributorAutoConfiguration.java new file mode 100644 index 000000000000..75fda9deb7f4 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverHealthContributorAutoConfiguration.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.cassandra; + +import java.util.Map; + +import com.datastax.oss.driver.api.core.CqlSession; + +import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration; +import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; +import org.springframework.boot.actuate.cassandra.CassandraDriverHealthIndicator; +import org.springframework.boot.actuate.health.HealthContributor; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +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; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for + * {@link CassandraDriverHealthIndicator}. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(CqlSession.class) +@ConditionalOnBean(CqlSession.class) +@ConditionalOnEnabledHealthIndicator("cassandra") +@AutoConfigureAfter({ CassandraAutoConfiguration.class, CassandraReactiveHealthContributorAutoConfiguration.class, + CassandraHealthContributorAutoConfiguration.class, + CassandraDriverReactiveHealthContributorAutoConfiguration.class }) +public class CassandraDriverHealthContributorAutoConfiguration + extends CompositeHealthContributorConfiguration { + + @Bean + @ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" }) + public HealthContributor cassandraHealthContributor(Map sessions) { + return createContributor(sessions); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverReactiveHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverReactiveHealthContributorAutoConfiguration.java new file mode 100644 index 000000000000..78964eefe95a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverReactiveHealthContributorAutoConfiguration.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.boot.actuate.autoconfigure.cassandra; + +import java.util.Map; + +import com.datastax.oss.driver.api.core.CqlSession; +import reactor.core.publisher.Flux; + +import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthContributorConfiguration; +import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; +import org.springframework.boot.actuate.cassandra.CassandraDriverReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ReactiveHealthContributor; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +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; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for + * {@link CassandraDriverReactiveHealthIndicator}. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass({ CqlSession.class, Flux.class }) +@ConditionalOnBean(CqlSession.class) +@ConditionalOnEnabledHealthIndicator("cassandra") +@AutoConfigureAfter({ CassandraAutoConfiguration.class, CassandraReactiveHealthContributorAutoConfiguration.class, + CassandraHealthContributorAutoConfiguration.class }) +public class CassandraDriverReactiveHealthContributorAutoConfiguration + extends CompositeReactiveHealthContributorConfiguration { + + @Bean + @ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" }) + public ReactiveHealthContributor cassandraHealthContributor(Map sessions) { + return createContributor(sessions); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index e297520518c5..c1e508919367 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -7,6 +7,8 @@ org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfigurat org.springframework.boot.actuate.autoconfigure.cache.CachesEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.cassandra.CassandraHealthContributorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.cassandra.CassandraReactiveHealthContributorAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.cassandra.CassandraDriverHealthContributorAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.cassandra.CassandraDriverReactiveHealthContributorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet.CloudFoundryActuatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive.ReactiveCloudFoundryActuatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpointAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverHealthContributorAutoConfigurationTests.java new file mode 100644 index 000000000000..5bdb9348809c --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverHealthContributorAutoConfigurationTests.java @@ -0,0 +1,89 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.cassandra; + +import com.datastax.oss.driver.api.core.CqlSession; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration; +import org.springframework.boot.actuate.cassandra.CassandraDriverHealthIndicator; +import org.springframework.boot.actuate.cassandra.CassandraDriverReactiveHealthIndicator; +import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator; +import org.springframework.boot.actuate.cassandra.CassandraReactiveHealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.core.ReactiveCassandraOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CassandraDriverHealthContributorAutoConfiguration}. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +class CassandraDriverHealthContributorAutoConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withBean(CqlSession.class, () -> mock(CqlSession.class)).withConfiguration(AutoConfigurations.of( + CassandraDriverHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class)); + + @Test + void runShouldCreateDriverIndicator() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(CassandraDriverHealthIndicator.class) + .hasBean("cassandraHealthContributor").doesNotHaveBean(CassandraHealthIndicator.class) + .doesNotHaveBean(CassandraReactiveHealthIndicator.class) + .doesNotHaveBean(CassandraDriverReactiveHealthIndicator.class)); + } + + @Test + void runWhenDisabledShouldNotCreateDriverIndicator() { + this.contextRunner.withPropertyValues("management.health.cassandra.enabled:false") + .run((context) -> assertThat(context).doesNotHaveBean(CassandraDriverHealthIndicator.class) + .doesNotHaveBean("cassandraHealthContributor")); + } + + @Test + void runWhenSpringDataPresentShouldNotCreateDriverIndicator() { + this.contextRunner.withConfiguration(AutoConfigurations.of(CassandraHealthContributorAutoConfiguration.class)) + .withBean(CassandraOperations.class, () -> mock(CassandraOperations.class)) + .run((context) -> assertThat(context).doesNotHaveBean(CassandraDriverHealthIndicator.class) + .hasSingleBean(CassandraHealthIndicator.class).hasBean("cassandraHealthContributor")); + } + + @Test + void runWhenReactorPresentShouldNotCreateDriverIndicator() { + this.contextRunner + .withConfiguration( + AutoConfigurations.of(CassandraDriverReactiveHealthContributorAutoConfiguration.class)) + .run((context) -> assertThat(context).doesNotHaveBean(CassandraDriverHealthIndicator.class) + .hasSingleBean(CassandraDriverReactiveHealthIndicator.class) + .hasBean("cassandraHealthContributor")); + } + + @Test + void runWhenSpringDataAndReactorPresentShouldNotCreateDriverIndicator() { + this.contextRunner + .withConfiguration(AutoConfigurations.of(CassandraReactiveHealthContributorAutoConfiguration.class)) + .withBean(ReactiveCassandraOperations.class, () -> mock(ReactiveCassandraOperations.class)) + .run((context) -> assertThat(context).doesNotHaveBean(CassandraDriverHealthIndicator.class) + .hasSingleBean(CassandraReactiveHealthIndicator.class).hasBean("cassandraHealthContributor")); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverReactiveHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverReactiveHealthContributorAutoConfigurationTests.java new file mode 100644 index 000000000000..bf2d8edb36ee --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraDriverReactiveHealthContributorAutoConfigurationTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.cassandra; + +import com.datastax.oss.driver.api.core.CqlSession; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration; +import org.springframework.boot.actuate.cassandra.CassandraDriverReactiveHealthIndicator; +import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator; +import org.springframework.boot.actuate.cassandra.CassandraReactiveHealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.core.ReactiveCassandraOperations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CassandraDriverReactiveHealthContributorAutoConfiguration}. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +class CassandraDriverReactiveHealthContributorAutoConfigurationTests { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withBean(CqlSession.class, () -> mock(CqlSession.class)) + .withConfiguration(AutoConfigurations.of(CassandraDriverReactiveHealthContributorAutoConfiguration.class, + HealthContributorAutoConfiguration.class)); + + @Test + void runShouldCreateDriverReactiveIndicator() { + this.contextRunner + .run((context) -> assertThat(context).hasSingleBean(CassandraDriverReactiveHealthIndicator.class) + .hasBean("cassandraHealthContributor").doesNotHaveBean(CassandraHealthIndicator.class) + .doesNotHaveBean(CassandraReactiveHealthIndicator.class)); + } + + @Test + void runWhenDisabledShouldNotCreateDriverReactiveIndicator() { + this.contextRunner.withPropertyValues("management.health.cassandra.enabled:false") + .run((context) -> assertThat(context).doesNotHaveBean(CassandraDriverReactiveHealthIndicator.class) + .doesNotHaveBean("cassandraHealthContributor")); + } + + @Test + void runWhenSpringDataPresentShouldNotCreateDriverReactiveIndicator() { + this.contextRunner.withConfiguration(AutoConfigurations.of(CassandraHealthContributorAutoConfiguration.class)) + .withBean(CassandraOperations.class, () -> mock(CassandraOperations.class)) + .run((context) -> assertThat(context).doesNotHaveBean(CassandraDriverReactiveHealthIndicator.class) + .hasSingleBean(CassandraHealthIndicator.class).hasBean("cassandraHealthContributor")); + } + + @Test + void runWhenSpringDataAndReactorPresentShouldNotCreateDriverReactiveIndicator() { + this.contextRunner + .withConfiguration(AutoConfigurations.of(CassandraReactiveHealthContributorAutoConfiguration.class)) + .withBean(ReactiveCassandraOperations.class, () -> mock(ReactiveCassandraOperations.class)) + .run((context) -> assertThat(context).doesNotHaveBean(CassandraDriverReactiveHealthIndicator.class) + .hasSingleBean(CassandraReactiveHealthIndicator.class).hasBean("cassandraHealthContributor")); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/build.gradle b/spring-boot-project/spring-boot-actuator/build.gradle index daf51b16e63d..74805708b8eb 100644 --- a/spring-boot-project/spring-boot-actuator/build.gradle +++ b/spring-boot-project/spring-boot-actuator/build.gradle @@ -14,6 +14,7 @@ dependencies { implementation(project(":spring-boot-project:spring-boot")) optional(platform(project(":spring-boot-project:spring-boot-dependencies"))) + optional("com.datastax.oss:java-driver-core") optional("com.fasterxml.jackson.core:jackson-databind") optional("com.github.ben-manes.caffeine:caffeine") optional("com.hazelcast:hazelcast") diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicator.java new file mode 100644 index 000000000000..75f262d51fc7 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicator.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.cassandra; + +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; + +import org.springframework.boot.actuate.health.AbstractHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.util.Assert; + +/** + * Simple implementation of a {@link HealthIndicator} returning status information for + * Cassandra data stores. + * + * This health indicator is automatically used when Spring Data Cassandra is not present, + * but the Cassandra driver is. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +public class CassandraDriverHealthIndicator extends AbstractHealthIndicator { + + private static final SimpleStatement SELECT = SimpleStatement + .newInstance("SELECT release_version FROM system.local").setConsistencyLevel(ConsistencyLevel.LOCAL_ONE); + + private final CqlSession session; + + /** + * Create a new {@link CassandraDriverHealthIndicator} instance. + * @param session the {@link CqlSession}. + */ + public CassandraDriverHealthIndicator(CqlSession session) { + super("Cassandra health check failed"); + Assert.notNull(session, "session must not be null"); + this.session = session; + } + + @Override + protected void doHealthCheck(Health.Builder builder) throws Exception { + Row row = this.session.execute(SELECT).one(); + builder.up(); + if (row != null && !row.isNull(0)) { + builder.withDetail("version", row.getString(0)); + } + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicator.java new file mode 100644 index 000000000000..04533afdc14d --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicator.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.boot.actuate.cassandra; + +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import reactor.core.publisher.Mono; + +import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.util.Assert; + +/** + * Simple implementation of a {@link ReactiveHealthIndicator} returning status information + * for Cassandra data stores. + * + * This health indicator is automatically used when Spring Data Cassandra is not present, + * but the Cassandra driver is. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +public class CassandraDriverReactiveHealthIndicator extends AbstractReactiveHealthIndicator { + + private static final SimpleStatement SELECT = SimpleStatement + .newInstance("SELECT release_version FROM system.local").setConsistencyLevel(ConsistencyLevel.LOCAL_ONE); + + private final CqlSession session; + + /** + * Create a new {@link CassandraHealthIndicator} instance. + * @param session the {@link CqlSession}. + */ + public CassandraDriverReactiveHealthIndicator(CqlSession session) { + super("Cassandra health check failed"); + Assert.notNull(session, "session must not be null"); + this.session = session; + } + + @Override + protected Mono doHealthCheck(Health.Builder builder) { + return Mono.from(this.session.executeReactive(SELECT)) + .map((row) -> builder.up().withDetail("version", row.getString(0)).build()); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicatorTests.java new file mode 100644 index 000000000000..875edd64c34f --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicatorTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.cassandra; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.DriverTimeoutException; +import com.datastax.oss.driver.api.core.cql.ResultSet; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link CassandraDriverHealthIndicator}. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +class CassandraDriverHealthIndicatorTests { + + @Test + void createWhenCqlSessionIsNullShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> new CassandraDriverHealthIndicator(null)); + } + + @Test + void healthWithCassandraUp() { + CqlSession session = mock(CqlSession.class); + ResultSet resultSet = mock(ResultSet.class); + Row row = mock(Row.class); + given(session.execute(any(SimpleStatement.class))).willReturn(resultSet); + given(resultSet.one()).willReturn(row); + given(row.isNull(0)).willReturn(false); + given(row.getString(0)).willReturn("1.0.0"); + CassandraDriverHealthIndicator healthIndicator = new CassandraDriverHealthIndicator(session); + Health health = healthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails().get("version")).isEqualTo("1.0.0"); + } + + @Test + void healthWithCassandraDown() { + CqlSession session = mock(CqlSession.class); + given(session.execute(any(SimpleStatement.class))).willThrow(new DriverTimeoutException("Test Exception")); + CassandraDriverHealthIndicator healthIndicator = new CassandraDriverHealthIndicator(session); + Health health = healthIndicator.health(); + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails().get("error")) + .isEqualTo(DriverTimeoutException.class.getName() + ": Test Exception"); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicatorTests.java new file mode 100644 index 000000000000..a6c1537091b8 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicatorTests.java @@ -0,0 +1,108 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.boot.actuate.cassandra; + +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveResultSet; +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveRow; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.DriverTimeoutException; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import org.junit.jupiter.api.Test; +import org.mockito.stubbing.Answer; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.doAnswer; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.mock; + +/** + * Tests for {@link CassandraDriverReactiveHealthIndicator}. + * + * @author Alexandre Dutra + * @since 2.4.0 + */ +class CassandraDriverReactiveHealthIndicatorTests { + + @Test + void createWhenCqlSessionIsNullShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> new CassandraDriverReactiveHealthIndicator(null)); + } + + @Test + void testCassandraIsUp() { + CqlSession session = mock(CqlSession.class); + ReactiveResultSet results = mock(ReactiveResultSet.class); + ReactiveRow row = mock(ReactiveRow.class); + given(session.executeReactive(any(SimpleStatement.class))).willReturn(results); + doAnswer(mockReactiveResultSetBehavior(row)).when(results).subscribe(any()); + given(row.getString(0)).willReturn("6.0.0"); + + CassandraDriverReactiveHealthIndicator cassandraReactiveHealthIndicator = new CassandraDriverReactiveHealthIndicator( + session); + Mono health = cassandraReactiveHealthIndicator.health(); + StepVerifier.create(health).consumeNextWith((h) -> { + assertThat(h.getStatus()).isEqualTo(Status.UP); + assertThat(h.getDetails()).containsOnlyKeys("version"); + assertThat(h.getDetails().get("version")).isEqualTo("6.0.0"); + }).verifyComplete(); + } + + @Test + void testCassandraIsDown() { + CqlSession session = mock(CqlSession.class); + given(session.executeReactive(any(SimpleStatement.class))) + .willThrow(new DriverTimeoutException("Test Exception")); + + CassandraDriverReactiveHealthIndicator cassandraReactiveHealthIndicator = new CassandraDriverReactiveHealthIndicator( + session); + Mono health = cassandraReactiveHealthIndicator.health(); + StepVerifier.create(health).consumeNextWith((h) -> { + assertThat(h.getStatus()).isEqualTo(Status.DOWN); + assertThat(h.getDetails()).containsOnlyKeys("error"); + assertThat(h.getDetails().get("error")) + .isEqualTo(DriverTimeoutException.class.getName() + ": Test Exception"); + }).verifyComplete(); + } + + private Answer mockReactiveResultSetBehavior(ReactiveRow row) { + return (invocation) -> { + Subscriber subscriber = invocation.getArgument(0); + Subscription s = new Subscription() { + @Override + public void request(long n) { + subscriber.onNext(row); + subscriber.onComplete(); + } + + @Override + public void cancel() { + } + }; + subscriber.onSubscribe(s); + return null; + }; + } + +}