11package io.sentry.spring.boot4
22
33import com.google.common.truth.Truth.assertThat
4+ import io.opentelemetry.api.OpenTelemetry
45import io.sentry.ITransportFactory
56import io.sentry.NoOpTransportFactory
67import io.sentry.okhttp.SentryOkHttpEventListener
78import io.sentry.okhttp.SentryOkHttpInterceptor
9+ import io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider
10+ import io.sentry.opentelemetry.agent.AgentMarker
811import java.util.concurrent.TimeUnit
912import java.util.concurrent.atomic.AtomicBoolean
1013import kotlin.test.Test
@@ -23,7 +26,7 @@ import org.springframework.context.annotation.Configuration
2326
2427class SentryOkHttpAutoConfigurationTest {
2528
26- private val contextRunner =
29+ private val baseContextRunner =
2730 ApplicationContextRunner ()
2831 .withUserConfiguration(TestApplication ::class .java, NoOpTransportConfiguration ::class .java)
2932 .withPropertyValues(
@@ -35,15 +38,54 @@ class SentryOkHttpAutoConfigurationTest {
3538 " sentry.enable-spotlight=false" ,
3639 )
3740
41+ private val contextRunner =
42+ baseContextRunner.withPropertyValues(" sentry.clients.ok-http-enabled=true" )
43+
44+ private val noOtelClassLoader =
45+ FilteredClassLoader (
46+ SentryAutoConfigurationCustomizerProvider ::class .java,
47+ AgentMarker ::class .java,
48+ )
49+
50+ private val noOtelContextRunner = contextRunner.withClassLoader(noOtelClassLoader)
51+
52+ @Test
53+ fun `does not modify clients unless explicitly enabled` () {
54+ for (properties in
55+ listOf (emptyArray<String >(), arrayOf(" sentry.clients.ok-http-enabled=false" ))) {
56+ val plainClient = OkHttpClient ()
57+ val manualClient =
58+ OkHttpClient .Builder ()
59+ .addInterceptor(SentryOkHttpInterceptor ())
60+ .eventListener(SentryOkHttpEventListener ())
61+ .build()
62+
63+ baseContextRunner
64+ .withClassLoader(noOtelClassLoader)
65+ .withPropertyValues(" sentry.dsn=http://key@localhost/proj" , * properties)
66+ .withBean(" plainClient" , OkHttpClient ::class .java, { plainClient })
67+ .withBean(" manualClient" , OkHttpClient ::class .java, { manualClient })
68+ .run { context ->
69+ assertThat(context.getBean(" plainClient" )).isSameInstanceAs(plainClient)
70+ assertThat(context.getBean(" manualClient" )).isSameInstanceAs(manualClient)
71+ assertThat(context.getBeansOfType(SentryOkHttpClientBeanPostProcessor ::class .java))
72+ .isEmpty()
73+ assertThat(context.getBean(SentryProperties ::class .java).clients.isOkHttpEnabled)
74+ .isFalse()
75+ }
76+ }
77+ }
78+
3879 @Test
3980 fun `instruments a Spring managed OkHttpClient` () {
40- contextRunner
81+ noOtelContextRunner
4182 .withPropertyValues(" sentry.dsn=http://key@localhost/proj" )
4283 .withUserConfiguration(OkHttpClientConfiguration ::class .java)
4384 .run { context ->
4485 val client = context.getBean(OkHttpClient ::class .java)
4586 val existingInterceptor = context.getBean(" existingInterceptor" , Interceptor ::class .java)
4687
88+ assertThat(context.getBean(SentryProperties ::class .java).clients.isOkHttpEnabled).isTrue()
4789 assertThat(client.connectTimeoutMillis).isEqualTo(1234 )
4890 assertThat(client.interceptors).contains(existingInterceptor)
4991 assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor >()).hasSize(1 )
@@ -62,7 +104,7 @@ class SentryOkHttpAutoConfigurationTest {
62104
63105 @Test
64106 fun `instruments every Spring managed OkHttpClient` () {
65- contextRunner
107+ noOtelContextRunner
66108 .withPropertyValues(" sentry.dsn=http://key@localhost/proj" )
67109 .withUserConfiguration(MultipleOkHttpClientsConfiguration ::class .java)
68110 .run { context ->
@@ -78,7 +120,7 @@ class SentryOkHttpAutoConfigurationTest {
78120
79121 @Test
80122 fun `does not duplicate an existing Sentry interceptor` () {
81- contextRunner
123+ noOtelContextRunner
82124 .withPropertyValues(" sentry.dsn=http://key@localhost/proj" )
83125 .withUserConfiguration(ManuallyInstrumentedOkHttpClientConfiguration ::class .java)
84126 .run { context ->
@@ -120,9 +162,42 @@ class SentryOkHttpAutoConfigurationTest {
120162 assertThat(client.interceptors).isEmpty()
121163 }
122164
165+ @Test
166+ fun `does not instrument when OpenTelemetry agent is present` () {
167+ contextRunner
168+ .withPropertyValues(" sentry.dsn=http://key@localhost/proj" )
169+ .withUserConfiguration(OkHttpClientConfiguration ::class .java)
170+ .run { context ->
171+ val client = context.getBean(OkHttpClient ::class .java)
172+
173+ assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor >()).isEmpty()
174+ assertThat(client.eventListenerFactory.create(mock()))
175+ .isNotInstanceOf(SentryOkHttpEventListener ::class .java)
176+ }
177+ }
178+
179+ @Test
180+ fun `does not instrument when Sentry OpenTelemetry integration is present` () {
181+ contextRunner
182+ .withClassLoader(FilteredClassLoader (AgentMarker ::class .java))
183+ .withPropertyValues(" sentry.dsn=http://key@localhost/proj" )
184+ .withUserConfiguration(
185+ OkHttpClientConfiguration ::class .java,
186+ OpenTelemetryConfiguration ::class .java,
187+ )
188+ .run { context ->
189+ val client = context.getBean(OkHttpClient ::class .java)
190+
191+ assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor >()).isEmpty()
192+ assertThat(client.eventListenerFactory.create(mock()))
193+ .isNotInstanceOf(SentryOkHttpEventListener ::class .java)
194+ }
195+ }
196+
123197 @Test
124198 fun `does not instrument OkHttpClient without a dsn` () {
125- contextRunner.withUserConfiguration(OkHttpClientConfiguration ::class .java).run { context ->
199+ noOtelContextRunner.withUserConfiguration(OkHttpClientConfiguration ::class .java).run { context
200+ ->
126201 val client = context.getBean(OkHttpClient ::class .java)
127202
128203 assertThat(client.interceptors.filterIsInstance<SentryOkHttpInterceptor >()).isEmpty()
@@ -133,15 +208,21 @@ class SentryOkHttpAutoConfigurationTest {
133208
134209 @Test
135210 fun `does not create a default OkHttpClient` () {
136- contextRunner .withPropertyValues(" sentry.dsn=http://key@localhost/proj" ).run { context ->
211+ noOtelContextRunner .withPropertyValues(" sentry.dsn=http://key@localhost/proj" ).run { context ->
137212 assertThat(context.getBeansOfType(OkHttpClient ::class .java)).isEmpty()
138213 }
139214 }
140215
141216 @Test
142217 fun `does not instrument when sentry-okhttp is not on the classpath` () {
143218 contextRunner
144- .withClassLoader(FilteredClassLoader (SentryOkHttpInterceptor ::class .java))
219+ .withClassLoader(
220+ FilteredClassLoader (
221+ SentryOkHttpInterceptor ::class .java,
222+ SentryAutoConfigurationCustomizerProvider ::class .java,
223+ AgentMarker ::class .java,
224+ )
225+ )
145226 .withPropertyValues(" sentry.dsn=http://key@localhost/proj" )
146227 .withUserConfiguration(OkHttpClientConfiguration ::class .java)
147228 .run { context ->
@@ -154,6 +235,11 @@ class SentryOkHttpAutoConfigurationTest {
154235
155236 @Configuration(proxyBeanMethods = false ) @EnableAutoConfiguration open class TestApplication
156237
238+ @Configuration(proxyBeanMethods = false )
239+ open class OpenTelemetryConfiguration {
240+ @Bean open fun openTelemetry (): OpenTelemetry = OpenTelemetry .noop()
241+ }
242+
157243 @Configuration(proxyBeanMethods = false )
158244 open class NoOpTransportConfiguration {
159245 @Bean open fun noOpTransportFactory (): ITransportFactory = NoOpTransportFactory .getInstance()
0 commit comments