Skip to content

Commit 19e7005

Browse files
msobeckwilkinsona
authored andcommitted
Test Micrometer config to property exposure
See gh-33743
1 parent 637e190 commit 19e7005

19 files changed

+269
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2012-2023 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.metrics.export;
18+
19+
import java.lang.reflect.Method;
20+
import java.time.Duration;
21+
import java.util.ArrayList;
22+
import java.util.Arrays;
23+
import java.util.List;
24+
import java.util.stream.Collectors;
25+
26+
import org.junit.Assert;
27+
28+
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PropertiesConfigAdapter;
29+
30+
/**
31+
* Utility to test that all Micrometer config values are exposed via properties and
32+
* settable using the corresponding {@link PropertiesConfigAdapter} implementation.
33+
*
34+
* @author Mirko Sobeck
35+
*/
36+
public final class TestConfigsToPropertiesExposure {
37+
38+
private TestConfigsToPropertiesExposure() {
39+
40+
}
41+
42+
private static final List<Class<?>> classesForPropertiesList = Arrays.asList(Boolean.class, Byte.class,
43+
Character.class, Short.class, Integer.class, Long.class, Double.class, Float.class, String.class,
44+
Duration.class);
45+
46+
/**
47+
* Assertion to test if default methods of a given config are overridden by the
48+
* adapter which implements it. This can be an indicator for micrometer config fields,
49+
* that have been forgotten to expose via spring properties. Not overridden default
50+
* methods in adapters are the most common cause of forgotten field exposure, because
51+
* they do not for force an override.
52+
* @param config micrometer config
53+
* @param adapter adapter for properties {@link PropertiesConfigAdapter}
54+
* @param excludedConfigMethods config methods that should be excluded for the
55+
* assertion
56+
*/
57+
public static void assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(Class<?> config,
58+
Class<? extends PropertiesConfigAdapter<?>> adapter, String... excludedConfigMethods) {
59+
List<String> configDefaultMethodNames = Arrays.stream(config.getDeclaredMethods())
60+
.filter((method) -> method.isDefault() && isSettableUsingProperties(method.getReturnType()))
61+
.map(Method::getName).collect(Collectors.toList());
62+
63+
configDefaultMethodNames.removeAll(Arrays.stream(excludedConfigMethods).collect(Collectors.toList()));
64+
List<String> notOverriddenDefaultMethods = new ArrayList<>(configDefaultMethodNames);
65+
66+
Class<?> currentClass = adapter;
67+
// loop through adapter class and superclasses
68+
// to find not overridden config methods
69+
while (!Object.class.equals(currentClass)) {
70+
List<String> overriddenClassDefaultMethods = Arrays.stream(currentClass.getDeclaredMethods())
71+
.map(Method::getName).filter(configDefaultMethodNames::contains).collect(Collectors.toList());
72+
73+
notOverriddenDefaultMethods.removeAll(overriddenClassDefaultMethods);
74+
currentClass = currentClass.getSuperclass();
75+
}
76+
77+
if (notOverriddenDefaultMethods.size() >= 1) {
78+
Assert.fail(
79+
"Found config default methods that are not overridden by the related PropertiesConfigAdapter: \n"
80+
+ notOverriddenDefaultMethods + "\n"
81+
+ "This could be an indicator for not exposed properties fields.\n"
82+
+ "Please check if the fields are meant to be exposed and if not, "
83+
+ "exclude them from this test by providing them to the method.");
84+
}
85+
}
86+
87+
/**
88+
* Guess if a class can be set using properties. This will only catch the basic use
89+
* cases regarding the micrometer configs to filter out methods that are not likely to
90+
* be designed to be set via properties. <pre>
91+
* isSettableUsingProperties(String.class) = true
92+
* isSettableUsingProperties(boolean.class) = true
93+
* isSettableUsingProperties(Object.class) = false
94+
* </pre>
95+
* @param clazz Class
96+
* @return is likely to be settable using properties
97+
*/
98+
private static boolean isSettableUsingProperties(Class<?> clazz) {
99+
if (Void.TYPE.equals(clazz)) {
100+
return false;
101+
}
102+
103+
if (clazz.isPrimitive()) {
104+
return true;
105+
}
106+
107+
return classesForPropertiesList.contains(clazz);
108+
}
109+
110+
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/appoptics/AppOpticsPropertiesConfigAdapterTests.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,10 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.appoptics;
1818

19+
import io.micrometer.appoptics.AppOpticsConfig;
1920
import org.junit.jupiter.api.Test;
2021

22+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
2123
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapterTests;
2224

2325
import static org.assertj.core.api.Assertions.assertThat;
@@ -68,4 +70,10 @@ void whenPropertiesFloorTimesIsSetAdapterFloorTimesReturnsIt() {
6870
assertThat(createConfigAdapter(properties).floorTimes()).isTrue();
6971
}
7072

73+
@Test
74+
void allDefaultConfigMethodsAreOverriddenByAtlasPropertiesConfigAdapter() {
75+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(AppOpticsConfig.class,
76+
AppOpticsPropertiesConfigAdapter.class, "connectTimeout");
77+
}
78+
7179
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/atlas/AtlasPropertiesConfigAdapterTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
import java.time.Duration;
2020

21+
import com.netflix.spectator.atlas.AtlasConfig;
2122
import org.junit.jupiter.api.Test;
2223

24+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
25+
2326
import static org.assertj.core.api.Assertions.assertThat;
2427

2528
/**
@@ -130,4 +133,11 @@ void whenPropertiesLwcIgnorePublishStepIsSetAdapterLwcIgnorePublishStepReturnsIt
130133
assertThat(new AtlasPropertiesConfigAdapter(properties).lwcIgnorePublishStep()).isFalse();
131134
}
132135

136+
@Test
137+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
138+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(AtlasConfig.class,
139+
AtlasPropertiesConfigAdapter.class, "lwcIgnorePublishStep", "initialPollingDelay", "autoStart",
140+
"lwcStep", "validTagCharacters");
141+
}
142+
133143
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/datadog/DatadogPropertiesConfigAdapterTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.datadog;
1818

19+
import io.micrometer.datadog.DatadogConfig;
1920
import org.junit.jupiter.api.Test;
2021

22+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
2123
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapterTests;
2224

2325
import static org.assertj.core.api.Assertions.assertThat;
@@ -76,4 +78,10 @@ void whenPropertiesUriIsSetAdapterUriReturnsIt() {
7678
assertThat(createConfigAdapter(properties).uri()).isEqualTo("https://app.example.com/api/v1/series");
7779
}
7880

81+
@Test
82+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
83+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(DatadogConfig.class,
84+
DatadogPropertiesConfigAdapter.class);
85+
}
86+
7987
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/dynatrace/DynatracePropertiesConfigAdapterTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
import java.util.HashMap;
2020

2121
import io.micrometer.dynatrace.DynatraceApiVersion;
22+
import io.micrometer.dynatrace.DynatraceConfig;
2223
import org.junit.jupiter.api.Test;
2324

25+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
26+
2427
import static org.assertj.core.api.Assertions.assertThat;
2528

2629
/**
@@ -161,4 +164,10 @@ void defaultValues() {
161164
assertThat(properties.getGroup()).isNull();
162165
}
163166

167+
@Test
168+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
169+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(DynatraceConfig.class,
170+
DynatracePropertiesConfigAdapter.class, "documentType");
171+
}
172+
164173
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/elastic/ElasticPropertiesConfigAdapterTests.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,11 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;
1818

19+
import io.micrometer.elastic.ElasticConfig;
1920
import org.junit.jupiter.api.Test;
2021

22+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
23+
2124
import static org.assertj.core.api.Assertions.assertThat;
2225

2326
/**
@@ -97,4 +100,10 @@ void whenPropertiesApiKeyCredentialsIsSetAdapterPipelineReturnsIt() {
97100
assertThat(new ElasticPropertiesConfigAdapter(properties).apiKeyCredentials()).isEqualTo("secret");
98101
}
99102

103+
@Test
104+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
105+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(ElasticConfig.class,
106+
ElasticPropertiesConfigAdapter.class, "documentType");
107+
}
108+
100109
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/ganglia/GangliaPropertiesConfigAdapterTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
import java.util.concurrent.TimeUnit;
2121

2222
import info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode;
23+
import io.micrometer.ganglia.GangliaConfig;
2324
import org.junit.jupiter.api.Test;
2425

26+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
27+
2528
import static org.assertj.core.api.Assertions.assertThat;
2629

2730
/**
@@ -81,4 +84,10 @@ void whenPropertiesPortIsSetAdapterPortReturnsIt() {
8184
assertThat(new GangliaPropertiesConfigAdapter(properties).port()).isEqualTo(4242);
8285
}
8386

87+
@Test
88+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
89+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(GangliaConfig.class,
90+
GangliaPropertiesConfigAdapter.class, "protocolVersion");
91+
}
92+
8493
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/graphite/GraphitePropertiesConfigAdapterTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
import java.time.Duration;
2020
import java.util.concurrent.TimeUnit;
2121

22+
import io.micrometer.graphite.GraphiteConfig;
2223
import io.micrometer.graphite.GraphiteProtocol;
2324
import org.junit.jupiter.api.Test;
2425

26+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
27+
2528
import static org.assertj.core.api.Assertions.assertThat;
2629

2730
/**
@@ -94,4 +97,10 @@ void whenPropertiesTagsAsPrefixIsSetAdapterTagsAsPrefixReturnsIt() {
9497
assertThat(new GraphitePropertiesConfigAdapter(properties).tagsAsPrefix()).isEqualTo(new String[] { "worker" });
9598
}
9699

100+
@Test
101+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
102+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(GraphiteConfig.class,
103+
GraphitePropertiesConfigAdapter.class);
104+
}
105+
97106
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/humio/HumioPropertiesConfigAdapterTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
import java.util.Collections;
2020

21+
import io.micrometer.humio.HumioConfig;
2122
import org.junit.jupiter.api.Test;
2223

24+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
25+
2326
import static org.assertj.core.api.Assertions.assertThat;
2427

2528
/**
@@ -51,4 +54,10 @@ void whenPropertiesUriIsSetAdapterUriReturnsIt() {
5154
assertThat(new HumioPropertiesConfigAdapter(properties).uri()).isEqualTo("https://humio.example.com");
5255
}
5356

57+
@Test
58+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
59+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(HumioConfig.class,
60+
HumioPropertiesConfigAdapter.class, "connectTimeout");
61+
}
62+
5463
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxPropertiesConfigAdapterTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.influx;
1818

1919
import io.micrometer.influx.InfluxApiVersion;
20+
import io.micrometer.influx.InfluxConfig;
2021
import org.junit.jupiter.api.Test;
2122

23+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
24+
2225
import static org.assertj.core.api.Assertions.assertThat;
2326

2427
/**
@@ -58,4 +61,10 @@ void adaptInfluxV2BasicConfig() {
5861
assertThat(adapter.token()).isEqualTo("token");
5962
}
6063

64+
@Test
65+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
66+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(InfluxConfig.class,
67+
InfluxPropertiesConfigAdapter.class);
68+
}
69+
6170
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/jmx/JmxPropertiesConfigAdapterTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
import java.time.Duration;
2020

21+
import io.micrometer.jmx.JmxConfig;
2122
import org.junit.jupiter.api.Test;
2223

24+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
25+
2326
import static org.assertj.core.api.Assertions.assertThat;
2427

2528
/**
@@ -43,4 +46,10 @@ void whenPropertiesDomainIsSetAdapterDomainReturnsIt() {
4346
assertThat(new JmxPropertiesConfigAdapter(properties).domain()).isEqualTo("abc");
4447
}
4548

49+
@Test
50+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
51+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(JmxConfig.class,
52+
JmxPropertiesConfigAdapter.class);
53+
}
54+
4655
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/kairos/KairosPropertiesConfigAdapterTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.kairos;
1818

19+
import io.micrometer.kairos.KairosConfig;
1920
import org.junit.jupiter.api.Test;
2021

22+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
2123
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapterTests;
2224

2325
import static org.assertj.core.api.Assertions.assertThat;
@@ -62,4 +64,10 @@ void whenPropertiesPasswordIsSetAdapterPasswordReturnsIt() {
6264
assertThat(createConfigAdapter(properties).password()).isEqualTo("secret");
6365
}
6466

67+
@Test
68+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
69+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(KairosConfig.class,
70+
KairosPropertiesConfigAdapter.class);
71+
}
72+
6573
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/newrelic/NewRelicPropertiesConfigAdapterTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.newrelic;
1818

1919
import io.micrometer.newrelic.ClientProviderType;
20+
import io.micrometer.newrelic.NewRelicConfig;
2021
import org.junit.jupiter.api.Test;
2122

23+
import org.springframework.boot.actuate.autoconfigure.metrics.export.TestConfigsToPropertiesExposure;
2224
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapterTests;
2325

2426
import static org.assertj.core.api.Assertions.assertThat;
@@ -83,4 +85,10 @@ void whenPropertiesUriIsSetAdapterUriReturnsIt() {
8385
assertThat(createConfigAdapter(properties).uri()).isEqualTo("https://example.newrelic.com");
8486
}
8587

88+
@Test
89+
void allConfigDefaultMethodsAreOverriddenByAdapter() {
90+
TestConfigsToPropertiesExposure.assertThatAllConfigDefaultMethodsAreOverriddenByAdapter(NewRelicConfig.class,
91+
NewRelicPropertiesConfigAdapter.class);
92+
}
93+
8694
}

0 commit comments

Comments
 (0)