Skip to content

Commit 838dcbf

Browse files
runningcodeclaude
andcommitted
test(spring-boot): Tighten the Boot 3 tracing checks and use a plain test client
The unsampled-trace test now sends a request with an id of its own and rejects any transaction for it, whatever its trace, so a mishandled header that starts a new trace fails it. The sampled test checks that transactions carry their request, which that relies on. The sample_rand and deferred-sampling tests also check the envelope header's trace ID, as the sample's tests did. That check showed that the context's TestRestTemplate, built from the RestTemplateBuilder Sentry customizes, added trace headers of its own to the requests the tests sent. The suite now uses one built outside the context. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 566eb80 commit 838dcbf

2 files changed

Lines changed: 26 additions & 8 deletions

File tree

‎sentry-spring-boot-jakarta/src/integrationTest/kotlin/io/sentry/spring/boot/jakarta/integrationtest/DistributedTracingIntegrationTest.kt‎

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.google.common.truth.Truth.assertThat
44
import io.sentry.SentryEnvelope
55
import io.sentry.protocol.SentryId
66
import io.sentry.test.transaction
7+
import kotlin.random.Random
78
import kotlin.test.Test
89
import org.springframework.http.HttpEntity
910
import org.springframework.http.HttpHeaders
@@ -27,26 +28,26 @@ class DistributedTracingIntegrationTest : IntegrationTest() {
2728
)
2829
assertThat(status).isEqualTo(500)
2930

30-
awaitTransaction("GET /tracing/{id}", traceId)
31+
val incoming = awaitTransaction("GET /tracing/{id}", traceId).transaction()!!
32+
assertThat(incoming.request?.url).endsWith("/tracing/1")
3133
awaitTransaction("GET /person/{id}", traceId)
3234
}
3335

3436
@Test
3537
fun `unsampled trace sends no transactions`() {
3638
val traceId = SentryId()
39+
// An id only this request uses, because a mishandled header could start a trace of its own.
40+
val id = Random.nextLong(2, Long.MAX_VALUE)
3741
val status =
3842
get(
39-
"/tracing/1",
43+
"/tracing/$id",
4044
sentryTrace = "$traceId-424cffc8f94feeee-0",
4145
baggage =
4246
"sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=false,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET",
4347
)
4448
assertThat(status).isEqualTo(500)
4549

46-
server.assertNoEnvelope {
47-
isTransaction(it, "GET /tracing/{id}", traceId) ||
48-
isTransaction(it, "GET /person/{id}", traceId)
49-
}
50+
server.assertNoEnvelope { isForRequest(it, id) }
5051
}
5152

5253
@Test
@@ -63,6 +64,8 @@ class DistributedTracingIntegrationTest : IntegrationTest() {
6364

6465
val incoming = awaitTransaction("GET /tracing/{id}", traceId).header.traceContext!!
6566
val outgoing = awaitTransaction("GET /person/{id}", traceId).header.traceContext!!
67+
assertThat(incoming.traceId).isEqualTo(traceId)
68+
assertThat(outgoing.traceId).isEqualTo(traceId)
6669
assertThat(incoming.sampleRand).isNotNull()
6770
assertThat(outgoing.sampleRand).isEqualTo(incoming.sampleRand)
6871
}
@@ -81,6 +84,8 @@ class DistributedTracingIntegrationTest : IntegrationTest() {
8184

8285
val incoming = awaitTransaction("GET /tracing/{id}", traceId).header.traceContext!!
8386
val outgoing = awaitTransaction("GET /person/{id}", traceId).header.traceContext!!
87+
assertThat(incoming.traceId).isEqualTo(traceId)
88+
assertThat(outgoing.traceId).isEqualTo(traceId)
8489
assertThat(incoming.sampleRate).isNotEqualTo("0.5")
8590
assertThat(outgoing.sampleRate).isEqualTo(incoming.sampleRate)
8691
}
@@ -130,6 +135,12 @@ class DistributedTracingIntegrationTest : IntegrationTest() {
130135
isTransaction(it, name, traceId)
131136
}
132137

138+
/** Whether [envelope] is a transaction for `/tracing/[id]` or the `/person/[id]` it calls. */
139+
private fun isForRequest(envelope: SentryEnvelope, id: Long): Boolean {
140+
val url = envelope.transaction()?.request?.url ?: return false
141+
return url.endsWith("/tracing/$id") || url.endsWith("/person/$id")
142+
}
143+
133144
private fun isTransaction(envelope: SentryEnvelope, name: String, traceId: SentryId): Boolean {
134145
val transaction = envelope.transaction() ?: return false
135146
return transaction.transaction == name && transaction.contexts.trace?.traceId == traceId

‎sentry-spring-boot-jakarta/src/integrationTest/kotlin/io/sentry/spring/boot/jakarta/integrationtest/IntegrationTest.kt‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package io.sentry.spring.boot.jakarta.integrationtest
33
import io.sentry.test.MockSentryServer
44
import org.junit.Before
55
import org.junit.runner.RunWith
6-
import org.springframework.beans.factory.annotation.Autowired
76
import org.springframework.boot.test.context.SpringBootTest
87
import org.springframework.boot.test.web.client.TestRestTemplate
8+
import org.springframework.boot.test.web.server.LocalServerPort
9+
import org.springframework.boot.web.client.RestTemplateBuilder
910
import org.springframework.test.context.DynamicPropertyRegistry
1011
import org.springframework.test.context.DynamicPropertySource
1112
import org.springframework.test.context.junit4.SpringRunner
@@ -34,7 +35,13 @@ import org.springframework.test.context.junit4.SpringRunner
3435
],
3536
)
3637
abstract class IntegrationTest {
37-
@Autowired lateinit var restTemplate: TestRestTemplate
38+
@LocalServerPort private var port = 0
39+
40+
// The context's TestRestTemplate comes from the RestTemplateBuilder that Sentry customizes, so
41+
// it would add trace headers of its own to the requests tests send. Tests use a plain one.
42+
val restTemplate: TestRestTemplate by lazy {
43+
TestRestTemplate(RestTemplateBuilder().rootUri("http://localhost:$port"))
44+
}
3845

3946
@Before
4047
fun resetServer() {

0 commit comments

Comments
 (0)