From 2197f9b690f56a8694686c347a6df1d6f9e285b6 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 20 Oct 2026 17:03:10 +0200 Subject: [PATCH 1/2] test(spring-boot): Remove the log4j2 Boot samples' system tests The jakarta-log4j2 and 4-log4j2 samples' system tests were copies of the plain samples' tests, which now run in the modules' integrationTest suites. The only addition was a check that a logged error carries the Log4j integration. That's covered by SentryLog4j2AppenderAutoConfigurationTest, which checks the appender Spring Boot attaches, and by sentry-log4j2's own tests of what the appender sends. Both samples come off the system-test runner and the backend workflow. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/system-tests-backend.yml | 6 - .../build.gradle.kts | 30 --- .../src/test/kotlin/io/sentry/DummyTest.kt | 12 -- .../io/sentry/systemtest/CacheSystemTest.kt | 51 ----- .../DistributedTracingSystemTest.kt | 197 ------------------ .../systemtest/GraphqlGreetingSystemTest.kt | 46 ---- .../systemtest/GraphqlProjectSystemTest.kt | 66 ------ .../systemtest/GraphqlTaskSystemTest.kt | 50 ----- .../sentry/systemtest/KafkaQueueSystemTest.kt | 117 ----------- .../io/sentry/systemtest/MetricsSystemTest.kt | 51 ----- .../io/sentry/systemtest/PersonSystemTest.kt | 110 ---------- .../io/sentry/systemtest/TodoSystemTest.kt | 61 ------ .../src/test/resources/log4j2-test.xml | 13 -- .../build.gradle.kts | 29 --- .../src/test/kotlin/io/sentry/DummyTest.kt | 12 -- .../io/sentry/systemtest/CacheSystemTest.kt | 51 ----- .../DistributedTracingSystemTest.kt | 197 ------------------ .../systemtest/FeatureFlagSystemTest.kt | 102 --------- .../systemtest/GraphqlGreetingSystemTest.kt | 46 ---- .../systemtest/GraphqlProjectSystemTest.kt | 66 ------ .../systemtest/GraphqlTaskSystemTest.kt | 50 ----- .../sentry/systemtest/KafkaQueueSystemTest.kt | 117 ----------- .../io/sentry/systemtest/MetricsSystemTest.kt | 51 ----- .../io/sentry/systemtest/PersonSystemTest.kt | 110 ---------- .../io/sentry/systemtest/TodoSystemTest.kt | 61 ------ .../src/test/resources/log4j2-test.xml | 13 -- test/system-test-runner.py | 6 - 27 files changed, 1721 deletions(-) delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/DummyTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/resources/log4j2-test.xml delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/DummyTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/FeatureFlagSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt delete mode 100644 sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/resources/log4j2-test.xml diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index 16a1df3090d..1f89abff0c5 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -40,9 +40,6 @@ jobs: - sample: "sentry-samples-spring-boot-webflux" agent: "false" agent-auto-init: "true" - - sample: "sentry-samples-spring-boot-jakarta-log4j2" - agent: "false" - agent-auto-init: "true" - sample: "sentry-samples-spring-boot-jakarta-opentelemetry-noagent" agent: "false" agent-auto-init: "true" @@ -67,9 +64,6 @@ jobs: - sample: "sentry-samples-jul" agent: "false" agent-auto-init: "true" - - sample: "sentry-samples-spring-boot-4-log4j2" - agent: "false" - agent-auto-init: "true" - sample: "sentry-samples-spring-boot-4-webflux" agent: "false" agent-auto-init: "true" diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts index 06cbdbe9400..86ae3560d22 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts @@ -6,7 +6,6 @@ plugins { alias(libs.plugins.spring.dependency.management) alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.spring) - id("io.sentry.systemtest") } group = "io.sentry.sample.spring-boot-4-log4j2" @@ -67,33 +66,4 @@ dependencies { // database query tracing implementation(projects.sentryJdbc) runtimeOnly(libs.hsqldb) - - testImplementation(kotlin(Config.kotlinStdLib)) - testImplementation(projects.sentry) - testImplementation(projects.sentrySystemTestSupport) - testImplementation(libs.apollo3.kotlin) - testImplementation(libs.kotlin.test.junit) - testImplementation(libs.slf4j2.api) - testImplementation(libs.springboot4.starter.test) { - exclude(group = "org.junit.vintage", module = "junit-vintage-engine") - } -} - -tasks.register("systemTest").configure { - group = "verification" - description = "Runs the System tests" - - val test = project.extensions.getByType()["test"] - testClassesDirs = test.output.classesDirs - classpath = test.runtimeClasspath - - maxParallelForks = 1 - - filter { includeTestsMatching("io.sentry.systemtest*") } -} - -tasks.named("test").configure { - require(this is Test) - - filter { excludeTestsMatching("io.sentry.systemtest.*") } } diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/DummyTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/DummyTest.kt deleted file mode 100644 index 6f762b7e453..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/DummyTest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.sentry - -import kotlin.test.Test -import kotlin.test.assertTrue - -class DummyTest { - @Test - fun `the only test`() { - // only needed to have more than 0 tests and not fail the build - assertTrue(true) - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt deleted file mode 100644 index b45e9c10853..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class CacheSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `cache put and get produce spans`() { - val restClient = testHelper.restClient - - // Save a todo (triggers @CachePut -> cache.put span) - val todo = Todo(1L, "test-todo", false) - restClient.saveCachedTodo(todo) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "cache.put") - } - - testHelper.reset() - - // Get the todo (triggers @Cacheable -> cache.get span, should be a hit) - restClient.getCachedTodo(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "cache.get") - } - } - - @Test - fun `cache evict produces span`() { - val restClient = testHelper.restClient - - restClient.deleteCachedTodo(1L) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "cache.evict") - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt deleted file mode 100644 index 3cd16003024..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt +++ /dev/null @@ -1,197 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.protocol.SentryId -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals -import org.junit.Before - -class DistributedTracingSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `get person distributed tracing`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-1", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /tracing/{id}" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /person/{id}" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - } - - @Test - fun `get person distributed tracing with sampled false`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-0", - "baggage" to - "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", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - testHelper.ensureNoTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /tracing/{id}" - } - - testHelper.ensureNoTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /person/{id}" - } - } - - @Test - fun `get person distributed tracing without sample_rand`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-1", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - var sampleRand1: String? = null - var sampleRand2: String? = null - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /tracing/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRand1 = envelopeHeader.traceContext?.sampleRand - } - - matches - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /person/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRand2 = envelopeHeader.traceContext?.sampleRand - } - - matches - } - - assertEquals(sampleRand1, sampleRand2) - } - - @Test - fun `get person distributed tracing updates sample_rate on deferred decision`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=0.5,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - var sampleRate1: String? = null - var sampleRate2: String? = null - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /tracing/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRate1 = envelopeHeader.traceContext?.sampleRate - } - - matches - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /person/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRate2 = envelopeHeader.traceContext?.sampleRate - } - - matches - } - - assertEquals(sampleRate1, sampleRate2) - assertNotEquals(sampleRate1, "0.5") - } - - @Test - fun `create person distributed tracing`() { - val traceId = SentryId() - val restClient = testHelper.restClient - val person = Person("firstA", "lastB") - val returnedPerson = - restClient.createPersonDistributedTracing( - person, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-1", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(200, restClient.lastKnownStatusCode) - - assertEquals(person.firstName, returnedPerson!!.firstName) - assertEquals(person.lastName, returnedPerson!!.lastName) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "POST /tracing/" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "POST /person/" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt deleted file mode 100644 index 76a6024decc..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import org.junit.Before - -class GraphqlGreetingSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `greeting works`() { - val response = testHelper.graphqlClient.greet("world") - - testHelper.ensureNoErrors(response) - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.greeting", - ) - } - } - - @Test - fun `greeting error`() { - val response = testHelper.graphqlClient.greet("crash") - - testHelper.ensureErrorCount(response, 1) - testHelper.ensureErrorReceived { error -> - error.message?.message?.startsWith("Unresolved RuntimeException for executionId ") ?: false - } - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.greeting", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt deleted file mode 100644 index fca3956717c..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import org.junit.Before - -class GraphqlProjectSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `project query works`() { - val response = testHelper.graphqlClient.project("proj-slug") - - testHelper.ensureNoErrors(response) - assertEquals("proj-slug", response?.data?.project?.slug) - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.project", - ) - } - } - - @Test - fun `project mutation works`() { - val response = testHelper.graphqlClient.addProject("proj-slug") - - testHelper.ensureNoErrors(response) - assertNotNull(response?.data?.addProject) - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Mutation.addProject", - ) - } - } - - @Test - fun `project mutation error`() { - val response = testHelper.graphqlClient.addProject("addprojectcrash") - - testHelper.ensureErrorCount(response, 1) - assertNull(response?.data?.addProject) - testHelper.ensureErrorReceived { error -> - error.message?.message?.startsWith("Unresolved RuntimeException for executionId ") ?: false - } - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Mutation.addProject", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt deleted file mode 100644 index 940709c0778..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class GraphqlTaskSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `tasks and assignees query works`() { - val response = testHelper.graphqlClient.tasksAndAssignees("project-slug") - - testHelper.ensureNoErrors(response) - - assertEquals(5, response?.data?.tasks?.size) - - val firstTask = response?.data?.tasks?.firstOrNull() ?: throw RuntimeException("no task") - assertEquals("T1", firstTask.id) - assertEquals("A3", firstTask.assigneeId) - assertEquals("A3", firstTask.assignee?.id) - assertEquals("C3", firstTask.creatorId) - assertEquals("C3", firstTask.creator?.id) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.tasks", - ) && - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Task.assignee", - ) && - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Task.creator", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt deleted file mode 100644 index 43781cf2c56..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt +++ /dev/null @@ -1,117 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -/** - * System tests for Kafka queue instrumentation. - * - * Requires: - * - The sample app running with `--spring.profiles.active=kafka` - * - A Kafka broker at localhost:9092 - * - The mock Sentry server at localhost:8000 - */ -class KafkaQueueSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `producer endpoint creates queue publish span`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("test-message") - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "queue.publish") - } - } - - @Test - fun `consumer creates queue process transaction`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("test-consumer-message") - assertEquals(200, restClient.lastKnownStatusCode) - - // The consumer runs asynchronously, so wait for the queue.process transaction - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionHaveOp(transaction, "queue.process") - } - } - - @Test - fun `producer and consumer share same trace`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("trace-test-message") - assertEquals(200, restClient.lastKnownStatusCode) - - // Capture the trace ID from the producer transaction (has queue.publish span) - var producerTraceId: String? = null - testHelper.ensureTransactionReceived { transaction, _ -> - if (testHelper.doesTransactionContainSpanWithOp(transaction, "queue.publish")) { - producerTraceId = transaction.contexts.trace?.traceId?.toString() - true - } else { - false - } - } - - // Verify the consumer transaction has the same trace ID - // Use retryCount=3 since the consumer may take a moment to process - testHelper.ensureEnvelopeReceived(retryCount = 3) { envelopeString -> - val envelope = - testHelper.jsonSerializer.deserializeEnvelope(envelopeString.byteInputStream()) - ?: return@ensureEnvelopeReceived false - val txItem = - envelope.items.firstOrNull { it.header.type == io.sentry.SentryItemType.Transaction } - ?: return@ensureEnvelopeReceived false - val tx = - txItem.getTransaction(testHelper.jsonSerializer) ?: return@ensureEnvelopeReceived false - - tx.contexts.trace?.operation == "queue.process" && - tx.contexts.trace?.traceId?.toString() == producerTraceId - } - } - - @Test - fun `queue publish span has messaging attributes`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("attrs-test") - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - val span = transaction.spans.firstOrNull { it.op == "queue.publish" } - if (span == null) return@ensureTransactionReceived false - - val data = span.data ?: return@ensureTransactionReceived false - data["messaging.system"] == "kafka" && data["messaging.destination.name"] == "sentry-topic" - } - } - - @Test - fun `queue process transaction has messaging attributes`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("process-attrs-test") - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - if (!testHelper.doesTransactionHaveOp(transaction, "queue.process")) { - return@ensureTransactionReceived false - } - - val data = transaction.contexts.trace?.data ?: return@ensureTransactionReceived false - data["messaging.system"] == "kafka" && data["messaging.destination.name"] == "sentry-topic" - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt deleted file mode 100644 index 039d9d640c7..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class MetricsSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `count metric`() { - val restClient = testHelper.restClient - assertEquals("count metric increased", restClient.getCountMetric()) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureMetricsReceived { event, header -> - testHelper.doesContainMetric(event, "countMetric", "counter", 1.0) && - testHelper.doesMetricHaveAttribute(event, "countMetric", "user.type", "admin") && - testHelper.doesMetricHaveAttribute(event, "countMetric", "feature.version", 2) - } - } - - @Test - fun `gauge metric`() { - val restClient = testHelper.restClient - assertEquals("gauge metric tracked", restClient.getGaugeMetric(14)) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureMetricsReceived { event, header -> - testHelper.doesContainMetric(event, "memory.free", "gauge", 14.0) - } - } - - @Test - fun `distribution metric`() { - val restClient = testHelper.restClient - assertEquals("distribution metric tracked", restClient.getDistributionMetric(23)) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureMetricsReceived { event, header -> - testHelper.doesContainMetric(event, "distributionMetric", "distribution", 23.0) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt deleted file mode 100644 index 715c994bee8..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.protocol.FeatureFlag -import io.sentry.protocol.SentryId -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class PersonSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `get person fails`() { - val restClient = testHelper.restClient - restClient.getPerson(1L) - assertEquals(500, restClient.lastKnownStatusCode) - - testHelper.ensureErrorReceived { event -> - event.message?.formatted == "Trying person with id=1" && - event.sdk?.integrationSet?.contains("Log4j") == true && - testHelper.doesEventHaveFlag(event, "my-feature-flag", true) - } - - testHelper.ensureErrorReceived { event -> - testHelper.doesEventHaveExceptionMessage(event, "Something went wrong [id=1]") && - testHelper.doesEventHaveFlag(event, "my-feature-flag", true) - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionHave( - transaction, - op = "http.server", - featureFlag = FeatureFlag("flag.evaluation.transaction-feature-flag", true), - ) && - testHelper.doesTransactionHaveSpanWith( - transaction, - op = "spanCreatedThroughSentryApi", - featureFlag = FeatureFlag("flag.evaluation.my-feature-flag", true), - ) - } - - Thread.sleep(10000) - - testHelper.ensureLogsReceived { logs, envelopeHeader -> - testHelper.doesContainLogWithBody(logs, "warn Sentry logging") && - testHelper.doesContainLogWithBody(logs, "error Sentry logging") && - testHelper.doesContainLogWithBody(logs, "hello there world!") && - testHelper.doesLogWithBodyHaveAttribute( - logs, - "warn Sentry logging", - "user.type", - "admin", - ) && - testHelper.doesLogWithBodyHaveAttribute( - logs, - "warn Sentry logging", - "feature.version", - 2, - ) && - testHelper.doesLogWithBodyHaveAttribute(logs, "warn Sentry logging", "debug.enabled", true) - } - } - - @Test - fun `create person works`() { - val restClient = testHelper.restClient - val person = Person("firstA", "lastB") - val returnedPerson = restClient.createPerson(person) - assertEquals(200, restClient.lastKnownStatusCode) - - assertEquals(person.firstName, returnedPerson!!.firstName) - assertEquals(person.lastName, returnedPerson!!.lastName) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOp(transaction, "PersonService.create") && - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "db.query", - "insert into person (firstName, lastName) values (?, ?)", - ) - } - } - - @Test - fun `create person starts a profile linked to the transaction`() { - var profilerId: SentryId? = null - val restClient = testHelper.restClient - val person = Person("firstA", "lastB") - val returnedPerson = restClient.createPerson(person) - assertEquals(200, restClient.lastKnownStatusCode) - - assertEquals(person.firstName, returnedPerson!!.firstName) - assertEquals(person.lastName, returnedPerson!!.lastName) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - profilerId = transaction.contexts.profile?.profilerId - transaction.transaction == "POST /person/" - } - testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> - profileChunk.profilerId == profilerId - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt deleted file mode 100644 index d34485e1388..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class TodoSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `get todo works`() { - val restClient = testHelper.restClient - restClient.getTodo(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "http.client", - "GET https://jsonplaceholder.typicode.com/todos/1", - ) - } - } - - @Test - fun `get todo webclient works`() { - val restClient = testHelper.restClient - restClient.getTodoWebclient(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "http.client", - "GET https://jsonplaceholder.typicode.com/todos/1", - ) - } - } - - @Test - fun `get todo restclient works`() { - val restClient = testHelper.restClient - restClient.getTodoRestClient(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "http.client", - "GET https://jsonplaceholder.typicode.com/todos/1", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/resources/log4j2-test.xml b/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/resources/log4j2-test.xml deleted file mode 100644 index 915b9614ef7..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/src/test/resources/log4j2-test.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts index 3535e1232e2..06314c2b28f 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts @@ -6,7 +6,6 @@ plugins { alias(libs.plugins.spring.dependency.management) alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.spring) - id("io.sentry.systemtest") } group = "io.sentry.sample.spring-boot-jakarta-log4j2" @@ -72,32 +71,4 @@ dependencies { // database query tracing implementation(projects.sentryJdbc) runtimeOnly(libs.hsqldb) - - testImplementation(kotlin(Config.kotlinStdLib)) - testImplementation(projects.sentry) - testImplementation(projects.sentrySystemTestSupport) - testImplementation(libs.kotlin.test.junit) - testImplementation(libs.slf4j2.api) - testImplementation(libs.springboot3.starter.test) { - exclude(group = "org.junit.vintage", module = "junit-vintage-engine") - } -} - -tasks.register("systemTest").configure { - group = "verification" - description = "Runs the System tests" - - val test = project.extensions.getByType()["test"] - testClassesDirs = test.output.classesDirs - classpath = test.runtimeClasspath - - maxParallelForks = 1 - - filter { includeTestsMatching("io.sentry.systemtest*") } -} - -tasks.named("test").configure { - require(this is Test) - - filter { excludeTestsMatching("io.sentry.systemtest.*") } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/DummyTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/DummyTest.kt deleted file mode 100644 index 6f762b7e453..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/DummyTest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.sentry - -import kotlin.test.Test -import kotlin.test.assertTrue - -class DummyTest { - @Test - fun `the only test`() { - // only needed to have more than 0 tests and not fail the build - assertTrue(true) - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt deleted file mode 100644 index b45e9c10853..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/CacheSystemTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class CacheSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `cache put and get produce spans`() { - val restClient = testHelper.restClient - - // Save a todo (triggers @CachePut -> cache.put span) - val todo = Todo(1L, "test-todo", false) - restClient.saveCachedTodo(todo) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "cache.put") - } - - testHelper.reset() - - // Get the todo (triggers @Cacheable -> cache.get span, should be a hit) - restClient.getCachedTodo(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "cache.get") - } - } - - @Test - fun `cache evict produces span`() { - val restClient = testHelper.restClient - - restClient.deleteCachedTodo(1L) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "cache.evict") - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt deleted file mode 100644 index 3cd16003024..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/DistributedTracingSystemTest.kt +++ /dev/null @@ -1,197 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.protocol.SentryId -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals -import org.junit.Before - -class DistributedTracingSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `get person distributed tracing`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-1", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /tracing/{id}" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /person/{id}" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - } - - @Test - fun `get person distributed tracing with sampled false`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-0", - "baggage" to - "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", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - testHelper.ensureNoTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /tracing/{id}" - } - - testHelper.ensureNoTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /person/{id}" - } - } - - @Test - fun `get person distributed tracing without sample_rand`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-1", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - var sampleRand1: String? = null - var sampleRand2: String? = null - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /tracing/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRand1 = envelopeHeader.traceContext?.sampleRand - } - - matches - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /person/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRand2 = envelopeHeader.traceContext?.sampleRand - } - - matches - } - - assertEquals(sampleRand1, sampleRand2) - } - - @Test - fun `get person distributed tracing updates sample_rate on deferred decision`() { - val traceId = SentryId() - val restClient = testHelper.restClient - restClient.getPersonDistributedTracing( - 1L, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=0.5,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(500, restClient.lastKnownStatusCode) - - var sampleRate1: String? = null - var sampleRate2: String? = null - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /tracing/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRate1 = envelopeHeader.traceContext?.sampleRate - } - - matches - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - val matches = - transaction.transaction == "GET /person/{id}" && - envelopeHeader.traceContext!!.traceId == traceId && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - - if (matches) { - testHelper.logObject(envelopeHeader) - testHelper.logObject(transaction) - sampleRate2 = envelopeHeader.traceContext?.sampleRate - } - - matches - } - - assertEquals(sampleRate1, sampleRate2) - assertNotEquals(sampleRate1, "0.5") - } - - @Test - fun `create person distributed tracing`() { - val traceId = SentryId() - val restClient = testHelper.restClient - val person = Person("firstA", "lastB") - val returnedPerson = - restClient.createPersonDistributedTracing( - person, - mapOf( - "sentry-trace" to "$traceId-424cffc8f94feeee-1", - "baggage" to - "sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET", - ), - ) - assertEquals(200, restClient.lastKnownStatusCode) - - assertEquals(person.firstName, returnedPerson!!.firstName) - assertEquals(person.lastName, returnedPerson!!.lastName) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "POST /tracing/" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "POST /person/" && - testHelper.doesTransactionHaveTraceId(transaction, traceId.toString()) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/FeatureFlagSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/FeatureFlagSystemTest.kt deleted file mode 100644 index 10233e579e5..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/FeatureFlagSystemTest.kt +++ /dev/null @@ -1,102 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.protocol.FeatureFlag -import io.sentry.systemtest.util.FeatureFlagResponse -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import org.junit.Before - -class FeatureFlagSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `check feature flag includes flag in transaction`() { - val restClient = testHelper.restClient - val response: FeatureFlagResponse? = restClient.checkFeatureFlag("new-checkout-flow") - - assertEquals(200, restClient.lastKnownStatusCode) - assertNotNull(response) - assertEquals("new-checkout-flow", response!!.flagKey) - assertEquals(true, response.value) - - // Verify feature flag is included in the transaction - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /feature-flag/check/{flagKey}" && - testHelper.doesTransactionHave( - transaction, - op = "http.server", - featureFlag = FeatureFlag("flag.evaluation.new-checkout-flow", true), - ) - } - } - - @Test - fun `error with feature flag includes flag in error event and transaction`() { - val restClient = testHelper.restClient - restClient.errorWithFeatureFlag("beta-features") - assertEquals(500, restClient.lastKnownStatusCode) - - // Verify feature flag is included in the error event - testHelper.ensureErrorReceived { event -> - testHelper.doesEventHaveExceptionMessage( - event, - "Error occurred with feature flag: beta-features = true", - ) && testHelper.doesEventHaveFlag(event, "beta-features", true) - } - - // Verify feature flag is also included in the transaction - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /feature-flag/error/{flagKey}" && - testHelper.doesTransactionHave( - transaction, - op = "http.server", - featureFlag = FeatureFlag("flag.evaluation.beta-features", true), - ) - } - } - - @Test - fun `check non-existent feature flag returns default value`() { - val restClient = testHelper.restClient - val response: FeatureFlagResponse? = restClient.checkFeatureFlag("non-existent-flag") - - assertEquals(200, restClient.lastKnownStatusCode) - assertNotNull(response) - assertEquals("non-existent-flag", response!!.flagKey) - assertEquals(false, response.value) // Default value - - // Verify transaction is still created - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /feature-flag/check/{flagKey}" - } - } - - @Test - fun `check feature flag with false value`() { - val restClient = testHelper.restClient - val response: FeatureFlagResponse? = restClient.checkFeatureFlag("experimental-feature") - - assertEquals(200, restClient.lastKnownStatusCode) - assertNotNull(response) - assertEquals("experimental-feature", response!!.flagKey) - assertEquals(false, response.value) - - // Verify feature flag with false value is included in transaction - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - transaction.transaction == "GET /feature-flag/check/{flagKey}" && - testHelper.doesTransactionHave( - transaction, - op = "http.server", - featureFlag = FeatureFlag("flag.evaluation.experimental-feature", false), - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt deleted file mode 100644 index 76a6024decc..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlGreetingSystemTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import org.junit.Before - -class GraphqlGreetingSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `greeting works`() { - val response = testHelper.graphqlClient.greet("world") - - testHelper.ensureNoErrors(response) - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.greeting", - ) - } - } - - @Test - fun `greeting error`() { - val response = testHelper.graphqlClient.greet("crash") - - testHelper.ensureErrorCount(response, 1) - testHelper.ensureErrorReceived { error -> - error.message?.message?.startsWith("Unresolved RuntimeException for executionId ") ?: false - } - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.greeting", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt deleted file mode 100644 index fca3956717c..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlProjectSystemTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import org.junit.Before - -class GraphqlProjectSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `project query works`() { - val response = testHelper.graphqlClient.project("proj-slug") - - testHelper.ensureNoErrors(response) - assertEquals("proj-slug", response?.data?.project?.slug) - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.project", - ) - } - } - - @Test - fun `project mutation works`() { - val response = testHelper.graphqlClient.addProject("proj-slug") - - testHelper.ensureNoErrors(response) - assertNotNull(response?.data?.addProject) - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Mutation.addProject", - ) - } - } - - @Test - fun `project mutation error`() { - val response = testHelper.graphqlClient.addProject("addprojectcrash") - - testHelper.ensureErrorCount(response, 1) - assertNull(response?.data?.addProject) - testHelper.ensureErrorReceived { error -> - error.message?.message?.startsWith("Unresolved RuntimeException for executionId ") ?: false - } - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Mutation.addProject", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt deleted file mode 100644 index 940709c0778..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/GraphqlTaskSystemTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class GraphqlTaskSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `tasks and assignees query works`() { - val response = testHelper.graphqlClient.tasksAndAssignees("project-slug") - - testHelper.ensureNoErrors(response) - - assertEquals(5, response?.data?.tasks?.size) - - val firstTask = response?.data?.tasks?.firstOrNull() ?: throw RuntimeException("no task") - assertEquals("T1", firstTask.id) - assertEquals("A3", firstTask.assigneeId) - assertEquals("A3", firstTask.assignee?.id) - assertEquals("C3", firstTask.creatorId) - assertEquals("C3", firstTask.creator?.id) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Query.tasks", - ) && - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Task.assignee", - ) && - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "graphql", - "Task.creator", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt deleted file mode 100644 index 43781cf2c56..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/KafkaQueueSystemTest.kt +++ /dev/null @@ -1,117 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -/** - * System tests for Kafka queue instrumentation. - * - * Requires: - * - The sample app running with `--spring.profiles.active=kafka` - * - A Kafka broker at localhost:9092 - * - The mock Sentry server at localhost:8000 - */ -class KafkaQueueSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `producer endpoint creates queue publish span`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("test-message") - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionContainSpanWithOp(transaction, "queue.publish") - } - } - - @Test - fun `consumer creates queue process transaction`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("test-consumer-message") - assertEquals(200, restClient.lastKnownStatusCode) - - // The consumer runs asynchronously, so wait for the queue.process transaction - testHelper.ensureTransactionReceived { transaction, _ -> - testHelper.doesTransactionHaveOp(transaction, "queue.process") - } - } - - @Test - fun `producer and consumer share same trace`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("trace-test-message") - assertEquals(200, restClient.lastKnownStatusCode) - - // Capture the trace ID from the producer transaction (has queue.publish span) - var producerTraceId: String? = null - testHelper.ensureTransactionReceived { transaction, _ -> - if (testHelper.doesTransactionContainSpanWithOp(transaction, "queue.publish")) { - producerTraceId = transaction.contexts.trace?.traceId?.toString() - true - } else { - false - } - } - - // Verify the consumer transaction has the same trace ID - // Use retryCount=3 since the consumer may take a moment to process - testHelper.ensureEnvelopeReceived(retryCount = 3) { envelopeString -> - val envelope = - testHelper.jsonSerializer.deserializeEnvelope(envelopeString.byteInputStream()) - ?: return@ensureEnvelopeReceived false - val txItem = - envelope.items.firstOrNull { it.header.type == io.sentry.SentryItemType.Transaction } - ?: return@ensureEnvelopeReceived false - val tx = - txItem.getTransaction(testHelper.jsonSerializer) ?: return@ensureEnvelopeReceived false - - tx.contexts.trace?.operation == "queue.process" && - tx.contexts.trace?.traceId?.toString() == producerTraceId - } - } - - @Test - fun `queue publish span has messaging attributes`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("attrs-test") - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - val span = transaction.spans.firstOrNull { it.op == "queue.publish" } - if (span == null) return@ensureTransactionReceived false - - val data = span.data ?: return@ensureTransactionReceived false - data["messaging.system"] == "kafka" && data["messaging.destination.name"] == "sentry-topic" - } - } - - @Test - fun `queue process transaction has messaging attributes`() { - val restClient = testHelper.restClient - - restClient.produceKafkaMessage("process-attrs-test") - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, _ -> - if (!testHelper.doesTransactionHaveOp(transaction, "queue.process")) { - return@ensureTransactionReceived false - } - - val data = transaction.contexts.trace?.data ?: return@ensureTransactionReceived false - data["messaging.system"] == "kafka" && data["messaging.destination.name"] == "sentry-topic" - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt deleted file mode 100644 index 039d9d640c7..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class MetricsSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `count metric`() { - val restClient = testHelper.restClient - assertEquals("count metric increased", restClient.getCountMetric()) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureMetricsReceived { event, header -> - testHelper.doesContainMetric(event, "countMetric", "counter", 1.0) && - testHelper.doesMetricHaveAttribute(event, "countMetric", "user.type", "admin") && - testHelper.doesMetricHaveAttribute(event, "countMetric", "feature.version", 2) - } - } - - @Test - fun `gauge metric`() { - val restClient = testHelper.restClient - assertEquals("gauge metric tracked", restClient.getGaugeMetric(14)) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureMetricsReceived { event, header -> - testHelper.doesContainMetric(event, "memory.free", "gauge", 14.0) - } - } - - @Test - fun `distribution metric`() { - val restClient = testHelper.restClient - assertEquals("distribution metric tracked", restClient.getDistributionMetric(23)) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureMetricsReceived { event, header -> - testHelper.doesContainMetric(event, "distributionMetric", "distribution", 23.0) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt deleted file mode 100644 index 715c994bee8..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.protocol.FeatureFlag -import io.sentry.protocol.SentryId -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class PersonSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `get person fails`() { - val restClient = testHelper.restClient - restClient.getPerson(1L) - assertEquals(500, restClient.lastKnownStatusCode) - - testHelper.ensureErrorReceived { event -> - event.message?.formatted == "Trying person with id=1" && - event.sdk?.integrationSet?.contains("Log4j") == true && - testHelper.doesEventHaveFlag(event, "my-feature-flag", true) - } - - testHelper.ensureErrorReceived { event -> - testHelper.doesEventHaveExceptionMessage(event, "Something went wrong [id=1]") && - testHelper.doesEventHaveFlag(event, "my-feature-flag", true) - } - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionHave( - transaction, - op = "http.server", - featureFlag = FeatureFlag("flag.evaluation.transaction-feature-flag", true), - ) && - testHelper.doesTransactionHaveSpanWith( - transaction, - op = "spanCreatedThroughSentryApi", - featureFlag = FeatureFlag("flag.evaluation.my-feature-flag", true), - ) - } - - Thread.sleep(10000) - - testHelper.ensureLogsReceived { logs, envelopeHeader -> - testHelper.doesContainLogWithBody(logs, "warn Sentry logging") && - testHelper.doesContainLogWithBody(logs, "error Sentry logging") && - testHelper.doesContainLogWithBody(logs, "hello there world!") && - testHelper.doesLogWithBodyHaveAttribute( - logs, - "warn Sentry logging", - "user.type", - "admin", - ) && - testHelper.doesLogWithBodyHaveAttribute( - logs, - "warn Sentry logging", - "feature.version", - 2, - ) && - testHelper.doesLogWithBodyHaveAttribute(logs, "warn Sentry logging", "debug.enabled", true) - } - } - - @Test - fun `create person works`() { - val restClient = testHelper.restClient - val person = Person("firstA", "lastB") - val returnedPerson = restClient.createPerson(person) - assertEquals(200, restClient.lastKnownStatusCode) - - assertEquals(person.firstName, returnedPerson!!.firstName) - assertEquals(person.lastName, returnedPerson!!.lastName) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOp(transaction, "PersonService.create") && - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "db.query", - "insert into person (firstName, lastName) values (?, ?)", - ) - } - } - - @Test - fun `create person starts a profile linked to the transaction`() { - var profilerId: SentryId? = null - val restClient = testHelper.restClient - val person = Person("firstA", "lastB") - val returnedPerson = restClient.createPerson(person) - assertEquals(200, restClient.lastKnownStatusCode) - - assertEquals(person.firstName, returnedPerson!!.firstName) - assertEquals(person.lastName, returnedPerson!!.lastName) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - profilerId = transaction.contexts.profile?.profilerId - transaction.transaction == "POST /person/" - } - testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> - profileChunk.profilerId == profilerId - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt deleted file mode 100644 index d34485e1388..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/kotlin/io/sentry/systemtest/TodoSystemTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.sentry.systemtest - -import io.sentry.systemtest.util.TestHelper -import kotlin.test.Test -import kotlin.test.assertEquals -import org.junit.Before - -class TodoSystemTest { - lateinit var testHelper: TestHelper - - @Before - fun setup() { - testHelper = TestHelper("http://localhost:8080") - testHelper.reset() - } - - @Test - fun `get todo works`() { - val restClient = testHelper.restClient - restClient.getTodo(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "http.client", - "GET https://jsonplaceholder.typicode.com/todos/1", - ) - } - } - - @Test - fun `get todo webclient works`() { - val restClient = testHelper.restClient - restClient.getTodoWebclient(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "http.client", - "GET https://jsonplaceholder.typicode.com/todos/1", - ) - } - } - - @Test - fun `get todo restclient works`() { - val restClient = testHelper.restClient - restClient.getTodoRestClient(1L) - assertEquals(200, restClient.lastKnownStatusCode) - - testHelper.ensureTransactionReceived { transaction, envelopeHeader -> - testHelper.doesTransactionContainSpanWithOpAndDescription( - transaction, - "http.client", - "GET https://jsonplaceholder.typicode.com/todos/1", - ) - } - } -} diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/resources/log4j2-test.xml b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/resources/log4j2-test.xml deleted file mode 100644 index 915b9614ef7..00000000000 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/src/test/resources/log4j2-test.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/test/system-test-runner.py b/test/system-test-runner.py index b028525bbbe..6a5a67f21be 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -72,20 +72,16 @@ "sentry-samples-console", "sentry-samples-spring-boot-opentelemetry", "sentry-samples-spring-boot-opentelemetry-noagent", - "sentry-samples-spring-boot-jakarta-log4j2", "sentry-samples-spring-boot-jakarta-opentelemetry", "sentry-samples-spring-boot-jakarta-opentelemetry-noagent", - "sentry-samples-spring-boot-4-log4j2", "sentry-samples-spring-boot-4-opentelemetry", "sentry-samples-spring-boot-4-opentelemetry-noagent", } KAFKA_PROFILE_REQUIRED_MODULES = { "sentry-samples-spring-boot-opentelemetry", "sentry-samples-spring-boot-opentelemetry-noagent", - "sentry-samples-spring-boot-jakarta-log4j2", "sentry-samples-spring-boot-jakarta-opentelemetry", "sentry-samples-spring-boot-jakarta-opentelemetry-noagent", - "sentry-samples-spring-boot-4-log4j2", "sentry-samples-spring-boot-4-opentelemetry", "sentry-samples-spring-boot-4-opentelemetry-noagent", } @@ -852,12 +848,10 @@ def get_available_modules(self) -> List[ModuleConfig]: ModuleConfig("sentry-samples-spring-boot-opentelemetry", "true", "false", "false"), ModuleConfig("sentry-samples-spring-boot-webflux-jakarta", "false", "true", "false"), ModuleConfig("sentry-samples-spring-boot-webflux", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-jakarta-log4j2", "false", "true", "false"), ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"), ModuleConfig("sentry-samples-spring-boot-4-webflux", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-4-log4j2", "false", "true", "false"), ModuleConfig("sentry-samples-spring-boot-4-opentelemetry-noagent", "false", "true", "false"), ModuleConfig("sentry-samples-spring-boot-4-opentelemetry", "true", "true", "false"), ModuleConfig("sentry-samples-spring-boot-4-opentelemetry", "true", "false", "false"), From ca0c04337c2656af094631da68a57b3c9a754fc5 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 20 Oct 2026 20:51:58 +0200 Subject: [PATCH 2/2] test(spring-boot): Test the auto-configured Log4j2 appender end to end The log4j2 Boot samples' tests were removed as covered elsewhere, but nothing else sends an event through the Log4j2 appender that Spring Boot's auto-configuration attaches. integrationTestLog4j2, in sentry-spring-boot-jakarta and sentry-spring-boot-4, runs a servlet app with Log4j2 instead of Logback and checks that an error logged through SLF4J reaches Sentry with the request's feature flag and the Log4j integration, as the samples' Person test did. Gradle's module replacement swaps the starters, as Spring Boot documents. It only acts on a classpath with both, which only this suite has. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/spring-boot-3-matrix.yml | 1 + .github/workflows/spring-boot-4-matrix.yml | 2 +- build.gradle.kts | 2 + sentry-spring-boot-4/build.gradle.kts | 39 ++++++++++- .../IntegrationTestApplication.kt | 25 +++++++ .../Log4j2IntegrationTest.kt | 67 +++++++++++++++++++ sentry-spring-boot-jakarta/build.gradle.kts | 31 +++++++++ .../IntegrationTestApplication.kt | 28 ++++++++ .../Log4j2IntegrationTest.kt | 67 +++++++++++++++++++ 9 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/IntegrationTestApplication.kt create mode 100644 sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/Log4j2IntegrationTest.kt create mode 100644 sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/IntegrationTestApplication.kt create mode 100644 sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/Log4j2IntegrationTest.kt diff --git a/.github/workflows/spring-boot-3-matrix.yml b/.github/workflows/spring-boot-3-matrix.yml index d62317b44fc..c0cbeb13c29 100644 --- a/.github/workflows/spring-boot-3-matrix.yml +++ b/.github/workflows/spring-boot-3-matrix.yml @@ -85,6 +85,7 @@ jobs: run: | ./gradlew \ :sentry-spring-boot-jakarta:integrationTest \ + :sentry-spring-boot-jakarta:integrationTestLog4j2 \ :sentry-spring-boot-jakarta:integrationTestOtelAgent - name: Test sentry-samples-spring-boot-webflux-jakarta diff --git a/.github/workflows/spring-boot-4-matrix.yml b/.github/workflows/spring-boot-4-matrix.yml index b6674080ba2..1b3d924c67e 100644 --- a/.github/workflows/spring-boot-4-matrix.yml +++ b/.github/workflows/spring-boot-4-matrix.yml @@ -82,7 +82,7 @@ jobs: :sentry-opentelemetry:sentry-opentelemetry-agent:assemble - name: Test sentry-spring-boot-4 integration suite - run: ./gradlew :sentry-spring-boot-4:integrationTest + run: ./gradlew :sentry-spring-boot-4:integrationTest :sentry-spring-boot-4:integrationTestLog4j2 - name: Run sentry-samples-spring-boot-4-webflux run: | diff --git a/build.gradle.kts b/build.gradle.kts index f47a748f4eb..567fbecdf29 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -225,8 +225,10 @@ tasks.register("integrationTests") { dependsOn( ":sentry-spring-boot:integrationTest", ":sentry-spring-boot-jakarta:integrationTest", + ":sentry-spring-boot-jakarta:integrationTestLog4j2", ":sentry-spring-boot-jakarta:integrationTestOtelAgent", ":sentry-spring-boot-4:integrationTest", + ":sentry-spring-boot-4:integrationTestLog4j2", ) } diff --git a/sentry-spring-boot-4/build.gradle.kts b/sentry-spring-boot-4/build.gradle.kts index fb6e70c39df..219f94cfef2 100644 --- a/sentry-spring-boot-4/build.gradle.kts +++ b/sentry-spring-boot-4/build.gradle.kts @@ -157,6 +157,19 @@ kotlin { } } +// Swaps Logback for Log4j2 where a classpath has both starters, which only +// integrationTestLog4j2's has. +dependencies { + modules { + module("org.springframework.boot:spring-boot-starter-logging") { + replacedBy( + "org.springframework.boot:spring-boot-starter-log4j2", + "Use Log4j2 instead of Logback", + ) + } + } +} + testing { suites { // Runs a Spring Boot app in the test's JVM against MockSentryServer. It is a suite of its own @@ -195,6 +208,25 @@ testing { runtimeOnly(libs.hsqldb) } } + + // The servlet app with Log4j2 instead of Logback, so Sentry's auto-configuration attaches its + // Log4j2 appender. + register("integrationTestLog4j2") { + useJUnit() + dependencies { + implementation(platform(SpringBootPlugin.BOM_COORDINATES)) + implementation(projects.sentrySpringBoot4Starter) + implementation(projects.sentryLog4j2) + implementation(projects.sentryTestSupport) + implementation(libs.kotlin.test.junit) + implementation(libs.google.truth) + implementation("org.springframework.boot:spring-boot-starter-log4j2") + implementation(libs.springboot4.resttestclient) + implementation(libs.springboot4.starter.restclient) + implementation(libs.springboot4.starter.test) + implementation(libs.springboot4.starter.web) + } + } } } @@ -202,4 +234,9 @@ testing { // to resolve `#id` in a cache key. tasks.named("compileIntegrationTestKotlin") { compilerOptions.javaParameters = true } -tasks.named("check") { dependsOn(testing.suites.named("integrationTest")) } +tasks.named("check") { + dependsOn( + testing.suites.named("integrationTest"), + testing.suites.named("integrationTestLog4j2"), + ) +} diff --git a/sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/IntegrationTestApplication.kt b/sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/IntegrationTestApplication.kt new file mode 100644 index 00000000000..8236515c327 --- /dev/null +++ b/sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/IntegrationTestApplication.kt @@ -0,0 +1,25 @@ +package io.sentry.spring.boot4.log4j2integrationtest + +import io.sentry.Sentry +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@SpringBootApplication class IntegrationTestApplication + +@RestController +@RequestMapping("/person/") +class PersonController { + private val logger = LoggerFactory.getLogger(PersonController::class.java) + + /** Logs an error through SLF4J, which Log4j2 handles in this app, then fails the request. */ + @GetMapping("{id}") + fun person(@PathVariable("id") id: Long): String { + Sentry.addFeatureFlag("my-feature-flag", true) + logger.error("Trying person with id={}", id, RuntimeException("error while loading")) + throw IllegalArgumentException("Something went wrong [id=$id]") + } +} diff --git a/sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/Log4j2IntegrationTest.kt b/sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/Log4j2IntegrationTest.kt new file mode 100644 index 00000000000..a3ec6a8a9b9 --- /dev/null +++ b/sentry-spring-boot-4/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot4/log4j2integrationtest/Log4j2IntegrationTest.kt @@ -0,0 +1,67 @@ +package io.sentry.spring.boot4.log4j2integrationtest + +import com.google.common.truth.Truth.assertThat +import io.sentry.Sentry +import io.sentry.test.MockSentryServer +import io.sentry.test.event +import io.sentry.test.hasFlag +import kotlin.test.Test +import org.junit.runner.RunWith +import org.springframework.boot.restclient.RestTemplateBuilder +import org.springframework.boot.resttestclient.TestRestTemplate +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.test.context.DynamicPropertyRegistry +import org.springframework.test.context.DynamicPropertySource +import org.springframework.test.context.junit4.SpringRunner + +/** + * Runs [IntegrationTestApplication] with Log4j2 instead of Logback, so Sentry's auto-configuration + * attaches its Log4j2 appender, and asserts what reaches [server]. + */ +@RunWith(SpringRunner::class) +@SpringBootTest( + classes = [IntegrationTestApplication::class], + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + // Sentry only attaches its Log4j2 appender when asked to. + properties = ["sentry.traces-sample-rate=1.0", "sentry.logging.enabled=true"], +) +class Log4j2IntegrationTest { + @LocalServerPort private var port = 0 + + // The context's TestRestTemplate comes from the RestTemplateBuilder that Sentry customizes, so + // it would add trace headers of its own to the requests tests send. Tests use a plain one. + private val restTemplate: TestRestTemplate by lazy { + TestRestTemplate(RestTemplateBuilder().rootUri("http://localhost:$port")) + } + + @Test + fun `error logged through Log4j2 reaches Sentry with the request's flag`() { + val response = restTemplate.getForEntity("/person/1", String::class.java) + assertThat(response.statusCode.value()).isEqualTo(500) + Sentry.flush(5000) + + val logged = + server.awaitEnvelope { it.event()?.message?.formatted == "Trying person with id=1" }.event()!! + assertThat(logged.hasFlag("my-feature-flag", true)).isTrue() + assertThat(logged.sdk!!.integrationSet).contains("Log4j") + + val thrown = + server + .awaitEnvelope { + it.event()?.exceptions?.any { e -> e.value == "Something went wrong [id=1]" } == true + } + .event()!! + assertThat(thrown.hasFlag("my-feature-flag", true)).isTrue() + } + + companion object { + val server = MockSentryServer() + + @JvmStatic + @DynamicPropertySource + fun sentryDsn(registry: DynamicPropertyRegistry) { + registry.add("sentry.dsn") { server.dsn } + } + } +} diff --git a/sentry-spring-boot-jakarta/build.gradle.kts b/sentry-spring-boot-jakarta/build.gradle.kts index 9991b33b512..215788d9eb9 100644 --- a/sentry-spring-boot-jakarta/build.gradle.kts +++ b/sentry-spring-boot-jakarta/build.gradle.kts @@ -168,6 +168,19 @@ dependencies { otelAgentJar(projects.sentryOpentelemetry.sentryOpentelemetryAgent) } +// Swaps Logback for Log4j2 where a classpath has both starters, which only +// integrationTestLog4j2's has. +dependencies { + modules { + module("org.springframework.boot:spring-boot-starter-logging") { + replacedBy( + "org.springframework.boot:spring-boot-starter-log4j2", + "Use Log4j2 instead of Logback", + ) + } + } +} + testing { suites { // Runs a Spring Boot app in the test's JVM against MockSentryServer. It is a suite of its own @@ -207,6 +220,23 @@ testing { } } + // The servlet app with Log4j2 instead of Logback, so Sentry's auto-configuration attaches its + // Log4j2 appender. + register("integrationTestLog4j2") { + useJUnit() + dependencies { + implementation(platform(SpringBootPlugin.BOM_COORDINATES)) + implementation(projects.sentrySpringBootStarterJakarta) + implementation(projects.sentryLog4j2) + implementation(projects.sentryTestSupport) + implementation(libs.kotlin.test.junit) + implementation(libs.google.truth) + implementation("org.springframework.boot:spring-boot-starter-log4j2") + implementation(libs.springboot3.starter.test) + implementation(libs.springboot3.starter.web) + } + } + register("integrationTestOtelAgent") { useJUnit() dependencies { @@ -235,6 +265,7 @@ tasks.named("compileIntegrationTestKotlin") { compilerOptions.jav tasks.named("check") { dependsOn( testing.suites.named("integrationTest"), + testing.suites.named("integrationTestLog4j2"), testing.suites.named("integrationTestOtelAgent"), ) } diff --git a/sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/IntegrationTestApplication.kt b/sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/IntegrationTestApplication.kt new file mode 100644 index 00000000000..0edf252da22 --- /dev/null +++ b/sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/IntegrationTestApplication.kt @@ -0,0 +1,28 @@ +package io.sentry.spring.boot.jakarta.log4j2integrationtest + +import io.sentry.Sentry +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +// Classes and methods are open because Spring proxies them and this module doesn't apply the +// kotlin-spring plugin. + +@SpringBootApplication open class IntegrationTestApplication + +@RestController +@RequestMapping("/person/") +open class PersonController { + private val logger = LoggerFactory.getLogger(PersonController::class.java) + + /** Logs an error through SLF4J, which Log4j2 handles in this app, then fails the request. */ + @GetMapping("{id}") + open fun person(@PathVariable("id") id: Long): String { + Sentry.addFeatureFlag("my-feature-flag", true) + logger.error("Trying person with id={}", id, RuntimeException("error while loading")) + throw IllegalArgumentException("Something went wrong [id=$id]") + } +} diff --git a/sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/Log4j2IntegrationTest.kt b/sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/Log4j2IntegrationTest.kt new file mode 100644 index 00000000000..c416731d43a --- /dev/null +++ b/sentry-spring-boot-jakarta/src/integrationTestLog4j2/kotlin/io/sentry/spring/boot/jakarta/log4j2integrationtest/Log4j2IntegrationTest.kt @@ -0,0 +1,67 @@ +package io.sentry.spring.boot.jakarta.log4j2integrationtest + +import com.google.common.truth.Truth.assertThat +import io.sentry.Sentry +import io.sentry.test.MockSentryServer +import io.sentry.test.event +import io.sentry.test.hasFlag +import kotlin.test.Test +import org.junit.runner.RunWith +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.boot.web.client.RestTemplateBuilder +import org.springframework.test.context.DynamicPropertyRegistry +import org.springframework.test.context.DynamicPropertySource +import org.springframework.test.context.junit4.SpringRunner + +/** + * Runs [IntegrationTestApplication] with Log4j2 instead of Logback, so Sentry's auto-configuration + * attaches its Log4j2 appender, and asserts what reaches [server]. + */ +@RunWith(SpringRunner::class) +@SpringBootTest( + classes = [IntegrationTestApplication::class], + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + // Sentry only attaches its Log4j2 appender when asked to. + properties = ["sentry.traces-sample-rate=1.0", "sentry.logging.enabled=true"], +) +class Log4j2IntegrationTest { + @LocalServerPort private var port = 0 + + // The context's TestRestTemplate comes from the RestTemplateBuilder that Sentry customizes, so + // it would add trace headers of its own to the requests tests send. Tests use a plain one. + private val restTemplate: TestRestTemplate by lazy { + TestRestTemplate(RestTemplateBuilder().rootUri("http://localhost:$port")) + } + + @Test + fun `error logged through Log4j2 reaches Sentry with the request's flag`() { + val response = restTemplate.getForEntity("/person/1", String::class.java) + assertThat(response.statusCode.value()).isEqualTo(500) + Sentry.flush(5000) + + val logged = + server.awaitEnvelope { it.event()?.message?.formatted == "Trying person with id=1" }.event()!! + assertThat(logged.hasFlag("my-feature-flag", true)).isTrue() + assertThat(logged.sdk!!.integrationSet).contains("Log4j") + + val thrown = + server + .awaitEnvelope { + it.event()?.exceptions?.any { e -> e.value == "Something went wrong [id=1]" } == true + } + .event()!! + assertThat(thrown.hasFlag("my-feature-flag", true)).isTrue() + } + + companion object { + val server = MockSentryServer() + + @JvmStatic + @DynamicPropertySource + fun sentryDsn(registry: DynamicPropertyRegistry) { + registry.add("sentry.dsn") { server.dsn } + } + } +}