Move MongoDB health support into spring-boot-mongodb

Previously the MongoDB health indicates used Spring Data's templates
(imperative and reactive) to access MongoDB. This prevented health
information from being available in apps using Mongo's Java Driver
directly.

This commit updates the health indicates so that they no longer
depend upon Spring Data MongoDB and instead using MongoDB's Java
Driver directly. As they no longer depend on Spring Data MongoDB,
the indicators have also moved from spring-boot-data-mongodb to
spring-boot-mongodb.

Closes gh-47051
This commit is contained in:
Andy Wilkinson
2025-09-05 15:07:52 +01:00
parent a977d260bd
commit 8d2c090af8
17 changed files with 199 additions and 139 deletions
@@ -19,7 +19,6 @@ plugins {
id "org.springframework.boot.auto-configuration"
id "org.springframework.boot.configuration-properties"
id "org.springframework.boot.deployed"
id "org.springframework.boot.docker-test"
id "org.springframework.boot.optional-dependencies"
}
@@ -35,18 +34,10 @@ dependencies {
compileOnly("com.google.code.findbugs:jsr305")
optional(project(":core:spring-boot-autoconfigure"))
optional(project(":module:spring-boot-health"))
optional(project(":module:spring-boot-reactor"))
optional("org.mongodb:mongodb-driver-reactivestreams")
optional("org.mongodb:mongodb-driver-sync")
dockerTestImplementation(project(":core:spring-boot-test"))
dockerTestImplementation(project(":test-support:spring-boot-docker-test-support"))
dockerTestImplementation("ch.qos.logback:logback-classic")
dockerTestImplementation("org.junit.jupiter:junit-jupiter")
dockerTestImplementation("org.testcontainers:junit-jupiter")
dockerTestImplementation("org.testcontainers:mongodb")
testCompileOnly("com.fasterxml.jackson.core:jackson-annotations")
testImplementation(project(":core:spring-boot-test"))
@@ -1,54 +0,0 @@
/*
* Copyright 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.health;
import org.bson.Document;
import reactor.core.publisher.Mono;
import org.springframework.boot.health.contributor.AbstractReactiveHealthIndicator;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.ReactiveHealthIndicator;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.util.Assert;
/**
* A {@link ReactiveHealthIndicator} for Mongo.
*
* @author Yulin Qin
* @since 4.0.0
*/
public class MongoReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
private final ReactiveMongoTemplate reactiveMongoTemplate;
public MongoReactiveHealthIndicator(ReactiveMongoTemplate reactiveMongoTemplate) {
super("Mongo health check failed");
Assert.notNull(reactiveMongoTemplate, "'reactiveMongoTemplate' must not be null");
this.reactiveMongoTemplate = reactiveMongoTemplate;
}
@Override
protected Mono<Health> doHealthCheck(Health.Builder builder) {
Mono<Document> buildInfo = this.reactiveMongoTemplate.executeCommand("{ hello: 1 }");
return buildInfo.map((document) -> up(builder, document));
}
private Health up(Health.Builder builder, Document document) {
return builder.up().withDetail("maxWireVersion", document.getInteger("maxWireVersion")).build();
}
}
@@ -2,5 +2,3 @@ org.springframework.boot.data.mongodb.autoconfigure.MongoDataAutoConfiguration
org.springframework.boot.data.mongodb.autoconfigure.MongoReactiveDataAutoConfiguration
org.springframework.boot.data.mongodb.autoconfigure.MongoReactiveRepositoriesAutoConfiguration
org.springframework.boot.data.mongodb.autoconfigure.MongoRepositoriesAutoConfiguration
org.springframework.boot.data.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration
org.springframework.boot.data.mongodb.autoconfigure.health.MongoReactiveHealthContributorAutoConfiguration
+2
View File
@@ -33,6 +33,7 @@ dependencies {
optional(project(":core:spring-boot-autoconfigure"))
optional(project(":core:spring-boot-docker-compose"))
optional(project(":core:spring-boot-testcontainers"))
optional(project(":module:spring-boot-health"))
optional(project(":module:spring-boot-micrometer-metrics"))
optional(project(":module:spring-boot-reactor"))
optional("io.netty:netty-transport")
@@ -49,6 +50,7 @@ dependencies {
testImplementation(project(":core:spring-boot-test"))
testImplementation(project(":test-support:spring-boot-test-support"))
testImplementation(testFixtures(project(":core:spring-boot-testcontainers")))
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.springframework.data:spring-data-mongodb")
testRuntimeOnly("ch.qos.logback:logback-classic")
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.health;
package org.springframework.boot.mongodb.health;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
@@ -23,6 +23,7 @@ import com.mongodb.ServerApi;
import com.mongodb.ServerApiVersion;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
@@ -31,7 +32,6 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.Status;
import org.springframework.boot.testsupport.container.TestImage;
import org.springframework.data.mongodb.core.MongoTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@@ -49,13 +49,21 @@ class MongoHealthIndicatorIntegrationTests {
@Test
void standardApi() {
Health health = mongoHealth();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertHealth(health);
}
@Test
void strictV1Api() {
Health health = mongoHealth(ServerApi.builder().strict(true).version(ServerApiVersion.V1).build());
assertHealth(health);
}
private void assertHealth(Health health) {
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsKey("maxWireVersion");
assertThat(health.getDetails()).hasEntrySatisfying("databases",
(databases) -> assertThat(databases).asInstanceOf(InstanceOfAssertFactories.LIST)
.containsExactlyInAnyOrder("local", "admin", "config"));
}
private Health mongoHealth() {
@@ -70,7 +78,7 @@ class MongoHealthIndicatorIntegrationTests {
}
MongoClientSettings settings = settingsBuilder.build();
MongoClient mongoClient = MongoClients.create(settings);
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(new MongoTemplate(mongoClient, "db"));
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoClient);
return healthIndicator.health(true);
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.health;
package org.springframework.boot.mongodb.health;
import java.time.Duration;
@@ -25,6 +25,7 @@ import com.mongodb.ServerApi;
import com.mongodb.ServerApiVersion;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
@@ -33,7 +34,6 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.Status;
import org.springframework.boot.testsupport.container.TestImage;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@@ -51,13 +51,13 @@ class MongoReactiveHealthIndicatorIntegrationTests {
@Test
void standardApi() {
Health health = mongoHealth();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertHealth(health);
}
@Test
void strictV1Api() {
Health health = mongoHealth(ServerApi.builder().strict(true).version(ServerApiVersion.V1).build());
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertHealth(health);
}
private Health mongoHealth() {
@@ -72,9 +72,16 @@ class MongoReactiveHealthIndicatorIntegrationTests {
}
MongoClientSettings settings = settingsBuilder.build();
MongoClient mongoClient = MongoClients.create(settings);
MongoReactiveHealthIndicator healthIndicator = new MongoReactiveHealthIndicator(
new ReactiveMongoTemplate(mongoClient, "db"));
MongoReactiveHealthIndicator healthIndicator = new MongoReactiveHealthIndicator(mongoClient);
return healthIndicator.health(true).block(Duration.ofSeconds(30));
}
private void assertHealth(Health health) {
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsKey("maxWireVersion");
assertThat(health.getDetails()).hasEntrySatisfying("databases",
(databases) -> assertThat(databases).asInstanceOf(InstanceOfAssertFactories.LIST)
.containsExactlyInAnyOrder("local", "admin", "config"));
}
}
@@ -14,7 +14,9 @@
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.autoconfigure.health;
package org.springframework.boot.mongodb.autoconfigure.health;
import com.mongodb.client.MongoClient;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -22,14 +24,12 @@ 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.data.mongodb.autoconfigure.MongoDataAutoConfiguration;
import org.springframework.boot.data.mongodb.health.MongoHealthIndicator;
import org.springframework.boot.health.autoconfigure.contributor.CompositeHealthContributorConfiguration;
import org.springframework.boot.health.autoconfigure.contributor.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.health.contributor.HealthContributor;
import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration;
import org.springframework.boot.mongodb.health.MongoHealthIndicator;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.MongoTemplate;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link MongoHealthIndicator}.
@@ -37,13 +37,12 @@ import org.springframework.data.mongodb.core.MongoTemplate;
* @author Stephane Nicoll
* @since 4.0.0
*/
@AutoConfiguration(after = { MongoReactiveHealthContributorAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoAutoConfiguration.class })
@ConditionalOnClass({ MongoTemplate.class, MongoHealthIndicator.class, ConditionalOnEnabledHealthIndicator.class })
@ConditionalOnBean(MongoTemplate.class)
@AutoConfiguration(after = { MongoReactiveHealthContributorAutoConfiguration.class, MongoAutoConfiguration.class })
@ConditionalOnClass({ MongoClient.class, MongoHealthIndicator.class, ConditionalOnEnabledHealthIndicator.class })
@ConditionalOnBean(MongoClient.class)
@ConditionalOnEnabledHealthIndicator("mongodb")
public final class MongoHealthContributorAutoConfiguration
extends CompositeHealthContributorConfiguration<MongoHealthIndicator, MongoTemplate> {
extends CompositeHealthContributorConfiguration<MongoHealthIndicator, MongoClient> {
MongoHealthContributorAutoConfiguration() {
super(MongoHealthIndicator::new);
@@ -52,7 +51,7 @@ public final class MongoHealthContributorAutoConfiguration
@Bean
@ConditionalOnMissingBean(name = { "mongoHealthIndicator", "mongoHealthContributor" })
HealthContributor mongoHealthContributor(ConfigurableListableBeanFactory beanFactory) {
return createContributor(beanFactory, MongoTemplate.class);
return createContributor(beanFactory, MongoClient.class);
}
}
@@ -14,8 +14,9 @@
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.autoconfigure.health;
package org.springframework.boot.mongodb.autoconfigure.health;
import com.mongodb.reactivestreams.client.MongoClient;
import reactor.core.publisher.Flux;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -24,13 +25,12 @@ 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.data.mongodb.autoconfigure.MongoReactiveDataAutoConfiguration;
import org.springframework.boot.data.mongodb.health.MongoReactiveHealthIndicator;
import org.springframework.boot.health.autoconfigure.contributor.CompositeReactiveHealthContributorConfiguration;
import org.springframework.boot.health.autoconfigure.contributor.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.health.contributor.ReactiveHealthContributor;
import org.springframework.boot.mongodb.autoconfigure.MongoReactiveAutoConfiguration;
import org.springframework.boot.mongodb.health.MongoReactiveHealthIndicator;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
/**
* {@link EnableAutoConfiguration Auto-configuration} for
@@ -39,13 +39,13 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
* @author Stephane Nicoll
* @since 4.0.0
*/
@AutoConfiguration(after = MongoReactiveDataAutoConfiguration.class)
@ConditionalOnClass({ ReactiveMongoTemplate.class, Flux.class, MongoReactiveHealthIndicator.class,
@AutoConfiguration(after = MongoReactiveAutoConfiguration.class)
@ConditionalOnClass({ MongoClient.class, Flux.class, MongoReactiveHealthIndicator.class,
ConditionalOnEnabledHealthIndicator.class })
@ConditionalOnBean(ReactiveMongoTemplate.class)
@ConditionalOnBean(MongoClient.class)
@ConditionalOnEnabledHealthIndicator("mongodb")
public final class MongoReactiveHealthContributorAutoConfiguration
extends CompositeReactiveHealthContributorConfiguration<MongoReactiveHealthIndicator, ReactiveMongoTemplate> {
extends CompositeReactiveHealthContributorConfiguration<MongoReactiveHealthIndicator, MongoClient> {
MongoReactiveHealthContributorAutoConfiguration() {
super(MongoReactiveHealthIndicator::new);
@@ -54,7 +54,7 @@ public final class MongoReactiveHealthContributorAutoConfiguration
@Bean
@ConditionalOnMissingBean(name = { "mongoHealthIndicator", "mongoHealthContributor" })
ReactiveHealthContributor mongoHealthContributor(ConfigurableListableBeanFactory beanFactory) {
return createContributor(beanFactory, ReactiveMongoTemplate.class);
return createContributor(beanFactory, MongoClient.class);
}
}
@@ -18,6 +18,6 @@
* Auto-configuration for Spring Data MongoDB health.
*/
@NullMarked
package org.springframework.boot.data.mongodb.autoconfigure.health;
package org.springframework.boot.mongodb.autoconfigure.health;
import org.jspecify.annotations.NullMarked;
@@ -14,37 +14,51 @@
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.health;
package org.springframework.boot.mongodb.health;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.mongodb.client.MongoClient;
import org.bson.Document;
import org.springframework.boot.health.contributor.AbstractHealthIndicator;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.HealthIndicator;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.Assert;
/**
* Simple implementation of a {@link HealthIndicator} returning status information for
* Mongo data stores.
* MongoDB.
*
* @author Christian Dupuis
* @since 4.0.0
*/
public class MongoHealthIndicator extends AbstractHealthIndicator {
private final MongoTemplate mongoTemplate;
private static final Document HELLO_COMMAND = Document.parse("{ hello: 1 }");
public MongoHealthIndicator(MongoTemplate mongoTemplate) {
private final MongoClient mongoClient;
public MongoHealthIndicator(MongoClient mongoClient) {
super("MongoDB health check failed");
Assert.notNull(mongoTemplate, "'mongoTemplate' must not be null");
this.mongoTemplate = mongoTemplate;
Assert.notNull(mongoClient, "'mongoClient' must not be null");
this.mongoClient = mongoClient;
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
Document result = this.mongoTemplate.executeCommand("{ hello: 1 }");
builder.up().withDetail("maxWireVersion", result.getInteger("maxWireVersion"));
Map<String, Object> details = new LinkedHashMap<>();
List<String> databases = new ArrayList<>();
details.put("databases", databases);
this.mongoClient.listDatabaseNames().forEach((database) -> {
Document result = this.mongoClient.getDatabase(database).runCommand(HELLO_COMMAND);
databases.add(database);
details.putIfAbsent("maxWireVersion", result.getInteger("maxWireVersion"));
});
builder.up().withDetails(details);
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.mongodb.health;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.mongodb.reactivestreams.client.MongoClient;
import org.bson.Document;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.boot.health.contributor.AbstractReactiveHealthIndicator;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.ReactiveHealthIndicator;
import org.springframework.util.Assert;
/**
* A {@link ReactiveHealthIndicator} for Mongo.
*
* @author Yulin Qin
* @since 4.0.0
*/
public class MongoReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
private static final Document HELLO_COMMAND = Document.parse("{ hello: 1 }");
private final MongoClient mongoClient;
public MongoReactiveHealthIndicator(MongoClient mongoClient) {
super("Mongo health check failed");
Assert.notNull(mongoClient, "'mongoClient' must not be null");
this.mongoClient = mongoClient;
}
@Override
protected Mono<Health> doHealthCheck(Health.Builder builder) {
Mono<Map<String, Object>> healthDetails = Flux.from(this.mongoClient.listDatabaseNames())
.flatMap((database) -> Mono.from(this.mongoClient.getDatabase(database).runCommand(HELLO_COMMAND))
.map((document) -> new HelloResponse(database, document)))
.collectList()
.map((responses) -> {
Map<String, Object> databaseDetails = new LinkedHashMap<>();
List<String> databases = new ArrayList<>();
databaseDetails.put("databases", databases);
for (HelloResponse response : responses) {
databases.add(response.database());
databaseDetails.putIfAbsent("maxWireVersion", response.document().getInteger("maxWireVersion"));
}
return databaseDetails;
});
return healthDetails.map((details) -> builder.up().withDetails(details).build());
}
private record HelloResponse(String database, Document document) {
}
}
@@ -18,6 +18,6 @@
* MongoDB health integration using Spring Data MongoDB.
*/
@NullMarked
package org.springframework.boot.data.mongodb.health;
package org.springframework.boot.mongodb.health;
import org.jspecify.annotations.NullMarked;
@@ -1,3 +1,5 @@
org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration
org.springframework.boot.mongodb.autoconfigure.MongoReactiveAutoConfiguration
org.springframework.boot.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration
org.springframework.boot.mongodb.autoconfigure.health.MongoReactiveHealthContributorAutoConfiguration
org.springframework.boot.mongodb.autoconfigure.metrics.MongoMetricsAutoConfiguration
@@ -19,10 +19,10 @@ package org.springframework.boot.data.mongodb.autoconfigure.health;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.data.mongodb.autoconfigure.MongoDataAutoConfiguration;
import org.springframework.boot.data.mongodb.health.MongoHealthIndicator;
import org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration;
import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration;
import org.springframework.boot.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration;
import org.springframework.boot.mongodb.health.MongoHealthIndicator;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class MongoHealthContributorAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
MongoHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class));
@Test
@@ -19,13 +19,13 @@ package org.springframework.boot.data.mongodb.autoconfigure.health;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.data.mongodb.autoconfigure.MongoDataAutoConfiguration;
import org.springframework.boot.data.mongodb.autoconfigure.MongoReactiveDataAutoConfiguration;
import org.springframework.boot.data.mongodb.health.MongoHealthIndicator;
import org.springframework.boot.data.mongodb.health.MongoReactiveHealthIndicator;
import org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration;
import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration;
import org.springframework.boot.mongodb.autoconfigure.MongoReactiveAutoConfiguration;
import org.springframework.boot.mongodb.autoconfigure.health.MongoHealthContributorAutoConfiguration;
import org.springframework.boot.mongodb.autoconfigure.health.MongoReactiveHealthContributorAutoConfiguration;
import org.springframework.boot.mongodb.health.MongoHealthIndicator;
import org.springframework.boot.mongodb.health.MongoReactiveHealthIndicator;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
@@ -38,8 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class MongoReactiveHealthContributorAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class,
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoReactiveAutoConfiguration.class,
MongoReactiveHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class));
@Test
@@ -14,51 +14,67 @@
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.health;
package org.springframework.boot.mongodb.health;
import java.util.List;
import java.util.function.Consumer;
import com.mongodb.MongoException;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.Status;
import org.springframework.data.mongodb.core.MongoTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MongoHealthIndicator}.
*
* @author Christian Dupuis
* @author Andy Wilkinson
*/
class MongoHealthIndicatorTests {
@Test
@SuppressWarnings("unchecked")
void mongoIsUp() {
Document commandResult = mock(Document.class);
given(commandResult.getInteger("maxWireVersion")).willReturn(10);
MongoTemplate mongoTemplate = mock(MongoTemplate.class);
given(mongoTemplate.executeCommand("{ hello: 1 }")).willReturn(commandResult);
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
MongoClient mongoClient = mock(MongoClient.class);
MongoIterable<String> databaseNames = mock(MongoIterable.class);
willAnswer((invocation) -> {
((Consumer<String>) invocation.getArgument(0)).accept("db");
return null;
}).given(databaseNames).forEach(any());
given(mongoClient.listDatabaseNames()).willReturn(databaseNames);
MongoDatabase mongoDatabase = mock(MongoDatabase.class);
given(mongoClient.getDatabase("db")).willReturn(mongoDatabase);
given(mongoDatabase.runCommand(Document.parse("{ hello: 1 }"))).willReturn(commandResult);
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoClient);
Health health = healthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsEntry("maxWireVersion", 10);
assertThat(health.getDetails()).containsEntry("databases", List.of("db"));
then(commandResult).should().getInteger("maxWireVersion");
then(mongoTemplate).should().executeCommand("{ hello: 1 }");
}
@Test
void mongoIsDown() {
MongoTemplate mongoTemplate = mock(MongoTemplate.class);
given(mongoTemplate.executeCommand("{ hello: 1 }")).willThrow(new MongoException("Connection failed"));
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
MongoClient mongoClient = mock(MongoClient.class);
given(mongoClient.listDatabaseNames()).willThrow(new MongoException("Connection failed"));
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoClient);
Health health = healthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat((String) health.getDetails().get("error")).contains("Connection failed");
then(mongoTemplate).should().executeCommand("{ hello: 1 }");
}
}
@@ -14,11 +14,14 @@
* limitations under the License.
*/
package org.springframework.boot.data.mongodb.health;
package org.springframework.boot.mongodb.health;
import java.time.Duration;
import java.util.List;
import com.mongodb.MongoException;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoDatabase;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
@@ -26,7 +29,6 @@ import reactor.test.StepVerifier;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.Status;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -40,27 +42,29 @@ import static org.mockito.Mockito.mock;
class MongoReactiveHealthIndicatorTests {
@Test
void testMongoIsUp() {
Document buildInfo = mock(Document.class);
given(buildInfo.getInteger("maxWireVersion")).willReturn(10);
ReactiveMongoTemplate reactiveMongoTemplate = mock(ReactiveMongoTemplate.class);
given(reactiveMongoTemplate.executeCommand("{ hello: 1 }")).willReturn(Mono.just(buildInfo));
MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(
reactiveMongoTemplate);
void mongoIsUp() {
MongoClient mongoClient = mock(MongoClient.class);
given(mongoClient.listDatabaseNames()).willReturn(Mono.just("db"));
MongoDatabase mongoDatabase = mock(MongoDatabase.class);
given(mongoClient.getDatabase("db")).willReturn(mongoDatabase);
Document commandResult = mock(Document.class);
given(mongoDatabase.runCommand(Document.parse("{ hello: 1 }"))).willReturn(Mono.just(commandResult));
given(commandResult.getInteger("maxWireVersion")).willReturn(10);
MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(mongoClient);
Mono<Health> health = mongoReactiveHealthIndicator.health();
StepVerifier.create(health).consumeNextWith((h) -> {
assertThat(h.getStatus()).isEqualTo(Status.UP);
assertThat(h.getDetails()).containsOnlyKeys("maxWireVersion");
assertThat(h.getDetails()).containsOnlyKeys("maxWireVersion", "databases");
assertThat(h.getDetails()).containsEntry("maxWireVersion", 10);
assertThat(h.getDetails()).containsEntry("databases", List.of("db"));
}).expectComplete().verify(Duration.ofSeconds(30));
}
@Test
void testMongoIsDown() {
ReactiveMongoTemplate reactiveMongoTemplate = mock(ReactiveMongoTemplate.class);
given(reactiveMongoTemplate.executeCommand("{ hello: 1 }")).willThrow(new MongoException("Connection failed"));
MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(
reactiveMongoTemplate);
void mongoIsDown() {
MongoClient mongoClient = mock(MongoClient.class);
given(mongoClient.listDatabaseNames()).willThrow(new MongoException("Connection failed"));
MongoReactiveHealthIndicator mongoReactiveHealthIndicator = new MongoReactiveHealthIndicator(mongoClient);
Mono<Health> health = mongoReactiveHealthIndicator.health();
StepVerifier.create(health).consumeNextWith((h) -> {
assertThat(h.getStatus()).isEqualTo(Status.DOWN);