Skip to content

Commit 6ae64f5

Browse files
Add tests
1 parent e928a35 commit 6ae64f5

File tree

11 files changed

+679
-3
lines changed

11 files changed

+679
-3
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.info;
1818

19+
import java.time.Duration;
1920
import java.util.Map;
2021
import java.util.Properties;
2122

2223
import org.junit.jupiter.api.Test;
2324

25+
import org.springframework.boot.actuate.autoconfigure.ssl.SslHealthIndicatorProperties;
2426
import org.springframework.boot.actuate.info.BuildInfoContributor;
2527
import org.springframework.boot.actuate.info.EnvironmentInfoContributor;
2628
import org.springframework.boot.actuate.info.GitInfoContributor;
@@ -29,12 +31,16 @@
2931
import org.springframework.boot.actuate.info.JavaInfoContributor;
3032
import org.springframework.boot.actuate.info.OsInfoContributor;
3133
import org.springframework.boot.actuate.info.ProcessInfoContributor;
34+
import org.springframework.boot.actuate.info.SslInfoContributor;
3235
import org.springframework.boot.autoconfigure.AutoConfigurations;
36+
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
3337
import org.springframework.boot.info.BuildProperties;
3438
import org.springframework.boot.info.GitProperties;
3539
import org.springframework.boot.info.JavaInfo;
3640
import org.springframework.boot.info.OsInfo;
3741
import org.springframework.boot.info.ProcessInfo;
42+
import org.springframework.boot.info.SslInfo;
43+
import org.springframework.boot.ssl.SslBundles;
3844
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3945
import org.springframework.context.annotation.Bean;
4046
import org.springframework.context.annotation.Configuration;
@@ -60,7 +66,8 @@ void envContributor() {
6066

6167
@Test
6268
void defaultInfoContributorsEnabled() {
63-
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(InfoContributor.class));
69+
this.contextRunner.run(
70+
(context) -> assertThat(context).doesNotHaveBean(InfoContributor.class).doesNotHaveBean(SslInfo.class));
6471
}
6572

6673
@Test
@@ -176,6 +183,54 @@ void processInfoContributor() {
176183
});
177184
}
178185

186+
@Test
187+
void sslInfoContributor() {
188+
this.contextRunner.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
189+
.withPropertyValues("management.info.ssl.enabled=true", "server.ssl.bundle=ssltest",
190+
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks")
191+
.run((context) -> {
192+
assertThat(context).hasSingleBean(SslInfoContributor.class);
193+
assertThat(context).hasSingleBean(SslInfo.class);
194+
Map<String, Object> content = invokeContributor(context.getBean(SslInfoContributor.class));
195+
assertThat(content).containsKey("ssl");
196+
assertThat(content.get("ssl")).isInstanceOf(SslInfo.class);
197+
});
198+
}
199+
200+
@Test
201+
void sslInfoContributorWithWarningThreshold() {
202+
this.contextRunner.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
203+
.withPropertyValues("management.info.ssl.enabled=true", "server.ssl.bundle=ssltest",
204+
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks",
205+
"management.health.ssl.certificate-validity-warning-threshold=1d")
206+
.run((context) -> {
207+
assertThat(context).hasSingleBean(SslInfoContributor.class);
208+
assertThat(context).hasSingleBean(SslInfo.class);
209+
assertThat(context).hasSingleBean(SslHealthIndicatorProperties.class);
210+
assertThat(context.getBean(SslHealthIndicatorProperties.class).getCertificateValidityWarningThreshold())
211+
.isEqualTo(Duration.ofDays(1));
212+
Map<String, Object> content = invokeContributor(context.getBean(SslInfoContributor.class));
213+
assertThat(content).containsKey("ssl");
214+
assertThat(content.get("ssl")).isInstanceOf(SslInfo.class);
215+
});
216+
}
217+
218+
@Test
219+
void customSslInfo() {
220+
this.contextRunner.withUserConfiguration(CustomSslInfoConfiguration.class)
221+
.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
222+
.withPropertyValues("management.info.ssl.enabled=true", "server.ssl.bundle=ssltest",
223+
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks")
224+
.run((context) -> {
225+
assertThat(context).hasSingleBean(SslInfoContributor.class);
226+
assertThat(context).hasSingleBean(SslInfo.class);
227+
assertThat(context.getBean(SslInfo.class)).isSameAs(context.getBean("customSslInfo"));
228+
Map<String, Object> content = invokeContributor(context.getBean(SslInfoContributor.class));
229+
assertThat(content).containsKey("ssl");
230+
assertThat(content.get("ssl")).isInstanceOf(SslInfo.class);
231+
});
232+
}
233+
179234
private Map<String, Object> invokeContributor(InfoContributor contributor) {
180235
Info.Builder builder = new Info.Builder();
181236
contributor.contribute(builder);
@@ -241,4 +296,14 @@ BuildInfoContributor customBuildInfoContributor() {
241296

242297
}
243298

299+
@Configuration(proxyBeanMethods = false)
300+
static class CustomSslInfoConfiguration {
301+
302+
@Bean
303+
SslInfo customSslInfo(SslBundles sslBundles) {
304+
return new SslInfo(sslBundles, Duration.ofDays(7));
305+
}
306+
307+
}
308+
244309
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.ssl;
18+
19+
import java.time.Duration;
20+
import java.util.List;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.boot.actuate.autoconfigure.ssl.SslHealthContributorAutoConfigurationTests.CustomSslInfoConfiguration.CustomSslHealthIndicator;
25+
import org.springframework.boot.actuate.health.Health;
26+
import org.springframework.boot.actuate.health.HealthIndicator;
27+
import org.springframework.boot.actuate.health.Status;
28+
import org.springframework.boot.actuate.ssl.SslHealthIndicator;
29+
import org.springframework.boot.autoconfigure.AutoConfigurations;
30+
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
31+
import org.springframework.boot.info.SslInfo;
32+
import org.springframework.boot.info.SslInfo.CertificateInfo;
33+
import org.springframework.boot.ssl.SslBundles;
34+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
35+
import org.springframework.context.annotation.Bean;
36+
import org.springframework.context.annotation.Configuration;
37+
38+
import static org.assertj.core.api.Assertions.assertThat;
39+
40+
/**
41+
* Tests for {@link SslHealthContributorAutoConfiguration}.
42+
*/
43+
class SslHealthContributorAutoConfigurationTests {
44+
45+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
46+
.withConfiguration(
47+
AutoConfigurations.of(SslHealthContributorAutoConfiguration.class, SslAutoConfiguration.class))
48+
.withPropertyValues("server.ssl.bundle=ssltest",
49+
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks");
50+
51+
@Test
52+
void beansShouldNotBeConfigured() {
53+
this.contextRunner.withPropertyValues("management.health.ssl.enabled=false")
54+
.run((context) -> assertThat(context).doesNotHaveBean(HealthIndicator.class)
55+
.doesNotHaveBean(SslInfo.class));
56+
}
57+
58+
@Test
59+
@SuppressWarnings("unchecked")
60+
void beansShouldBeConfigured() {
61+
this.contextRunner.run((context) -> {
62+
assertThat(context).hasSingleBean(SslHealthIndicator.class);
63+
assertThat(context).hasSingleBean(SslInfo.class);
64+
Health health = context.getBean(SslHealthIndicator.class).health();
65+
assertThat(health.getStatus()).isSameAs(Status.OUT_OF_SERVICE);
66+
assertThat(health.getDetails()).hasSize(1);
67+
List<CertificateInfo> certificates = (List<CertificateInfo>) health.getDetails().get("certificates");
68+
assertThat(certificates).hasSize(1);
69+
assertThat(certificates.get(0)).isInstanceOf(CertificateInfo.class);
70+
});
71+
}
72+
73+
@Test
74+
@SuppressWarnings("unchecked")
75+
void beansShouldBeConfiguredWithWarningThreshold() {
76+
this.contextRunner.withPropertyValues("management.health.ssl.certificate-validity-warning-threshold=1d")
77+
.run((context) -> {
78+
assertThat(context).hasSingleBean(SslHealthIndicator.class);
79+
assertThat(context).hasSingleBean(SslInfo.class);
80+
assertThat(context).hasSingleBean(SslHealthIndicatorProperties.class);
81+
assertThat(context.getBean(SslHealthIndicatorProperties.class).getCertificateValidityWarningThreshold())
82+
.isEqualTo(Duration.ofDays(1));
83+
Health health = context.getBean(SslHealthIndicator.class).health();
84+
assertThat(health.getStatus()).isSameAs(Status.OUT_OF_SERVICE);
85+
assertThat(health.getDetails()).hasSize(1);
86+
List<CertificateInfo> certificates = (List<CertificateInfo>) health.getDetails().get("certificates");
87+
assertThat(certificates).hasSize(1);
88+
assertThat(certificates.get(0)).isInstanceOf(CertificateInfo.class);
89+
});
90+
}
91+
92+
@Test
93+
@SuppressWarnings("unchecked")
94+
void customBeansShouldBeConfigured() {
95+
this.contextRunner.withUserConfiguration(CustomSslInfoConfiguration.class).run((context) -> {
96+
assertThat(context).hasSingleBean(SslHealthIndicator.class);
97+
assertThat(context.getBean(SslHealthIndicator.class))
98+
.isSameAs(context.getBean(CustomSslHealthIndicator.class));
99+
assertThat(context).hasSingleBean(SslInfo.class);
100+
assertThat(context.getBean(SslInfo.class)).isSameAs(context.getBean("customSslInfo"));
101+
Health health = context.getBean(SslHealthIndicator.class).health();
102+
assertThat(health.getStatus()).isSameAs(Status.OUT_OF_SERVICE);
103+
assertThat(health.getDetails()).hasSize(1);
104+
List<CertificateInfo> certificates = (List<CertificateInfo>) health.getDetails().get("certificates");
105+
assertThat(certificates).hasSize(1);
106+
assertThat(certificates.get(0)).isInstanceOf(CertificateInfo.class);
107+
});
108+
}
109+
110+
@Configuration(proxyBeanMethods = false)
111+
static class CustomSslInfoConfiguration {
112+
113+
@Bean
114+
SslHealthIndicator sslHealthIndicator(SslInfo sslInfo) {
115+
return new CustomSslHealthIndicator(sslInfo);
116+
}
117+
118+
@Bean
119+
SslInfo customSslInfo(SslBundles sslBundles) {
120+
return new SslInfo(sslBundles, Duration.ofDays(7));
121+
}
122+
123+
static class CustomSslHealthIndicator extends SslHealthIndicator {
124+
125+
CustomSslHealthIndicator(SslInfo sslInfo) {
126+
super(sslInfo);
127+
}
128+
129+
}
130+
131+
}
132+
133+
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/SslInfoContributor.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@
1616

1717
package org.springframework.boot.actuate.info;
1818

19+
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
20+
import org.springframework.aot.hint.RuntimeHints;
21+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
1922
import org.springframework.boot.actuate.info.Info.Builder;
23+
import org.springframework.boot.actuate.info.SslInfoContributor.SslInfoContributorRuntimeHints;
2024
import org.springframework.boot.info.SslInfo;
25+
import org.springframework.context.annotation.ImportRuntimeHints;
2126

2227
/**
2328
* An {@link InfoContributor} that exposes {@link SslInfo}.
2429
*
2530
* @author Jonatan Ivanov
2631
* @since 3.4.0
2732
*/
33+
@ImportRuntimeHints(SslInfoContributorRuntimeHints.class)
2834
public class SslInfoContributor implements InfoContributor {
2935

3036
private final SslInfo sslInfo;
@@ -38,4 +44,15 @@ public void contribute(Builder builder) {
3844
builder.withDetail("ssl", this.sslInfo);
3945
}
4046

47+
static class SslInfoContributorRuntimeHints implements RuntimeHintsRegistrar {
48+
49+
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
50+
51+
@Override
52+
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
53+
this.bindingRegistrar.registerReflectionHints(hints.reflection(), SslInfo.class);
54+
}
55+
56+
}
57+
4158
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ssl/SslHealthIndicator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ protected void doHealthCheck(Builder builder) throws Exception {
6969
}
7070
else if (statuses.contains(Validity.Status.WILL_EXPIRE_SOON)) {
7171
// TODO: Should we introduce Status.WARNING
72-
// (returns 200 but indicates that something is not right)?
72+
// (returns 200 but indicates that something is not right)?
7373
builder.status(WILL_EXPIRE_SOON_STATUS);
7474
}
7575
else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.info;
18+
19+
import java.time.Duration;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.aot.hint.MemberCategory;
24+
import org.springframework.aot.hint.RuntimeHints;
25+
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
26+
import org.springframework.boot.actuate.info.SslInfoContributor.SslInfoContributorRuntimeHints;
27+
import org.springframework.boot.info.SslInfo;
28+
import org.springframework.boot.ssl.DefaultSslBundleRegistry;
29+
import org.springframework.boot.ssl.SslBundle;
30+
import org.springframework.boot.ssl.SslBundles;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.mockito.Mockito.mock;
34+
35+
/**
36+
* Tests for {@link SslInfoContributor}.
37+
*
38+
* @author Jonatan Ivanov
39+
*/
40+
class SslInfoContributorTests {
41+
42+
@Test
43+
void sslInfoShouldBeAdded() {
44+
SslBundles sslBundles = new DefaultSslBundleRegistry("test", mock(SslBundle.class));
45+
SslInfo sslInfo = new SslInfo(sslBundles, Duration.ofDays(14));
46+
SslInfoContributor sslInfoContributor = new SslInfoContributor(sslInfo);
47+
Info.Builder builder = new Info.Builder();
48+
sslInfoContributor.contribute(builder);
49+
Info info = builder.build();
50+
assertThat(info.getDetails().get("ssl")).isInstanceOf(SslInfo.class);
51+
}
52+
53+
@Test
54+
void shouldRegisterHints() {
55+
RuntimeHints runtimeHints = new RuntimeHints();
56+
new SslInfoContributorRuntimeHints().registerHints(runtimeHints, getClass().getClassLoader());
57+
assertThat(RuntimeHintsPredicates.reflection()
58+
.onType(SslInfo.class)
59+
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
60+
.accepts(runtimeHints);
61+
}
62+
63+
}

0 commit comments

Comments
 (0)