Skip to content

Commit 2fe9357

Browse files
authored
Merge e531d9d into 0ceb6bf
2 parents 0ceb6bf + e531d9d commit 2fe9357

File tree

14 files changed

+1179
-107
lines changed

14 files changed

+1179
-107
lines changed

.github/workflows/system-tests-backend.yml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,27 @@ jobs:
2121
fail-fast: false
2222
matrix:
2323
sample: [ "sentry-samples-spring-boot-jakarta" ]
24-
agent: [ "0" ]
24+
agent: [ "false" ]
2525
agent-auto-init: [ "true" ]
2626
include:
2727
- sample: "sentry-samples-spring-boot"
2828
- sample: "sentry-samples-spring-boot-opentelemetry-noagent"
2929
- sample: "sentry-samples-spring-boot-opentelemetry"
30-
agent: "1"
30+
agent: "true"
3131
agent-auto-init: "true"
3232
- sample: "sentry-samples-spring-boot-opentelemetry"
33-
agent: "1"
33+
agent: "true"
3434
agent-auto-init: "false"
3535
- sample: "sentry-samples-spring-boot-webflux-jakarta"
3636
- sample: "sentry-samples-spring-boot-webflux"
3737
- sample: "sentry-samples-spring-boot-jakarta-opentelemetry-noagent"
3838
- sample: "sentry-samples-spring-boot-jakarta-opentelemetry"
39-
agent: "1"
39+
agent: "true"
4040
agent-auto-init: "true"
4141
- sample: "sentry-samples-spring-boot-jakarta-opentelemetry"
42-
agent: "1"
42+
agent: "true"
4343
agent-auto-init: "false"
44+
- sample: "sentry-samples-console"
4445
steps:
4546
- uses: actions/checkout@v4
4647
with:
@@ -90,17 +91,9 @@ jobs:
9091
-e '/.*"sentry-samples-android",/d' \
9192
build.gradle.kts
9293
93-
- name: Build server jar
94+
- name: Build and run system tests
9495
run: |
95-
./gradlew :sentry-samples:${{ matrix.sample }}:bootJar
96-
97-
- name: Build agent jar
98-
run: |
99-
./gradlew :sentry-opentelemetry:sentry-opentelemetry-agent:assemble
100-
101-
- name: Start server and run integration test for sentry-cli commands
102-
run: |
103-
test/system-test-run.sh "${{ matrix.sample }}" "${{ matrix.agent }}" "${{ matrix.agent-auto-init }}" "0"
96+
python3 test/system-test-runner.py test --module "${{ matrix.sample }}" --agent "${{ matrix.agent }}" --auto-init "${{ matrix.agent-auto-init }}" --build "true"
10497
10598
- name: Upload test results
10699
if: always()

Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleBenchmarkTestRelease assembleUiTestRelease assembleUiTestCriticalRelease createCoverageReports runUiTestCritical check preMerge publish
1+
.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleBenchmarkTestRelease assembleUiTestRelease assembleUiTestCriticalRelease createCoverageReports runUiTestCritical check preMerge publish systemtest systemtest-interactive
22

33
all: stop clean javadocs compile createCoverageReports
44
assembleBenchmarks: assembleBenchmarkTestRelease
@@ -59,6 +59,14 @@ createCoverageReports:
5959
./gradlew jacocoTestReport
6060
./gradlew koverXmlReportRelease
6161

62+
# Run system tests for sample applications
63+
systemtest:
64+
python3 test/system-test-runner.py test --all
65+
66+
# Run system tests with interactive module selection
67+
systemtest-interactive:
68+
python3 test/system-test-runner.py test --interactive
69+
6270
# Run tests and lint
6371
check:
6472
./gradlew check
Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,83 @@
1+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2+
13
plugins {
24
java
35
application
6+
kotlin("jvm")
47
alias(libs.plugins.gradle.versions)
8+
id("com.github.johnrengelman.shadow") version "8.1.1"
59
}
610

711
application { mainClass.set("io.sentry.samples.console.Main") }
812

13+
java.sourceCompatibility = JavaVersion.VERSION_17
14+
15+
java.targetCompatibility = JavaVersion.VERSION_17
16+
17+
repositories { mavenCentral() }
18+
919
configure<JavaPluginExtension> {
10-
sourceCompatibility = JavaVersion.VERSION_1_8
11-
targetCompatibility = JavaVersion.VERSION_1_8
20+
sourceCompatibility = JavaVersion.VERSION_17
21+
targetCompatibility = JavaVersion.VERSION_17
22+
}
23+
24+
tasks.withType<KotlinCompile>().configureEach {
25+
kotlinOptions.jvmTarget = JavaVersion.VERSION_17.toString()
26+
}
27+
28+
tasks.withType<KotlinCompile>().configureEach {
29+
kotlinOptions {
30+
freeCompilerArgs = listOf("-Xjsr305=strict")
31+
jvmTarget = JavaVersion.VERSION_17.toString()
32+
}
33+
}
34+
35+
dependencies {
36+
implementation(projects.sentry)
37+
38+
testImplementation(kotlin(Config.kotlinStdLib))
39+
testImplementation(projects.sentry)
40+
testImplementation(projects.sentrySystemTestSupport)
41+
testImplementation(libs.kotlin.test.junit)
42+
testImplementation(libs.slf4j.api)
43+
testImplementation(libs.slf4j.jdk14)
44+
}
45+
46+
// Configure the Shadow JAR (executable JAR with all dependencies)
47+
tasks.shadowJar {
48+
manifest { attributes["Main-Class"] = "io.sentry.samples.console.Main" }
49+
archiveClassifier.set("") // Remove the classifier so it replaces the regular JAR
50+
mergeServiceFiles()
1251
}
1352

14-
dependencies { implementation(projects.sentry) }
53+
// Make the regular jar task depend on shadowJar
54+
tasks.jar {
55+
enabled = false
56+
dependsOn(tasks.shadowJar)
57+
}
58+
59+
// Fix the startScripts task dependency
60+
tasks.startScripts { dependsOn(tasks.shadowJar) }
61+
62+
configure<SourceSetContainer> { test { java.srcDir("src/test/java") } }
63+
64+
tasks.register<Test>("systemTest").configure {
65+
group = "verification"
66+
description = "Runs the System tests"
67+
68+
outputs.upToDateWhen { false }
69+
70+
maxParallelForks = 1
71+
72+
// Cap JVM args per test
73+
minHeapSize = "128m"
74+
maxHeapSize = "1g"
75+
76+
filter { includeTestsMatching("io.sentry.systemtest*") }
77+
}
78+
79+
tasks.named("test").configure {
80+
require(this is Test)
81+
82+
filter { excludeTestsMatching("io.sentry.systemtest.*") }
83+
}

sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static void main(String[] args) throws InterruptedException {
2020
options -> {
2121
// NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in
2222
// your Sentry project/dashboard
23+
options.setEnableExternalConfiguration(true);
2324
options.setDsn(
2425
"https://[email protected]/5428563");
2526

@@ -66,10 +67,8 @@ public static void main(String[] args) throws InterruptedException {
6667
options.setDebug(true);
6768
// To change the verbosity, use:
6869
// By default it's DEBUG.
69-
options.setDiagnosticLevel(
70-
SentryLevel
71-
.ERROR); // A good option to have SDK debug log in prod is to use only level
72-
// ERROR here.
70+
// options.setDiagnosticLevel(SentryLevel.ERROR);
71+
// A good option to have SDK debug log in prod is to use only level ERROR here.
7372

7473
// Exclude frames from some packages from being "inApp" so are hidden by default in Sentry
7574
// UI:
@@ -83,15 +82,16 @@ public static void main(String[] args) throws InterruptedException {
8382
options.setTracesSampleRate(1.0); // set 0.5 to send 50% of traces
8483

8584
// Determine traces sample rate based on the sampling context
86-
options.setTracesSampler(
87-
context -> {
88-
// only 10% of transactions with "/product" prefix will be collected
89-
if (!context.getTransactionContext().getName().startsWith("/products")) {
90-
return 0.1;
91-
} else {
92-
return 0.5;
93-
}
94-
});
85+
// options.setTracesSampler(
86+
// context -> {
87+
// // only 10% of transactions with "/product" prefix will be collected
88+
// if (!context.getTransactionContext().getName().startsWith("/products"))
89+
// {
90+
// return 0.1;
91+
// } else {
92+
// return 0.5;
93+
// }
94+
// });
9595
});
9696

9797
Sentry.addBreadcrumb(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.sentry
2+
3+
import kotlin.test.Test
4+
import kotlin.test.assertTrue
5+
6+
class DummyTest {
7+
@Test
8+
fun `the only test`() {
9+
// only needed to have more than 0 tests and not fail the build
10+
assertTrue(true)
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package io.sentry.systemtest
2+
3+
import io.sentry.systemtest.util.TestHelper
4+
import java.util.concurrent.TimeUnit
5+
import org.junit.Assert.assertEquals
6+
import org.junit.Assert.assertTrue
7+
import org.junit.Before
8+
import org.junit.Test
9+
10+
class ConsoleApplicationSystemTest {
11+
lateinit var testHelper: TestHelper
12+
13+
@Before
14+
fun setup() {
15+
testHelper = TestHelper("http://localhost:8000")
16+
testHelper.reset()
17+
}
18+
19+
@Test
20+
fun `console application sends expected events when run as JAR`() {
21+
val jarFile = testHelper.findJar("sentry-samples-console")
22+
val process =
23+
testHelper.launch(
24+
jarFile,
25+
mapOf(
26+
"SENTRY_DSN" to testHelper.dsn,
27+
"SENTRY_TRACES_SAMPLE_RATE" to "1.0",
28+
"SENTRY_ENABLE_PRETTY_SERIALIZATION_OUTPUT" to "false",
29+
"SENTRY_DEBUG" to "true",
30+
),
31+
)
32+
33+
process.waitFor(30, TimeUnit.SECONDS)
34+
assertEquals(0, process.exitValue())
35+
36+
// Verify that we received the expected events
37+
verifyExpectedEvents()
38+
}
39+
40+
private fun verifyExpectedEvents() {
41+
// Verify we received a "Fatal message!" event
42+
testHelper.ensureErrorReceived { event ->
43+
event.message?.formatted == "Fatal message!" && event.level?.name == "FATAL"
44+
}
45+
46+
// Verify we received a "Some warning!" event
47+
testHelper.ensureErrorReceived { event ->
48+
event.message?.formatted == "Some warning!" && event.level?.name == "WARNING"
49+
}
50+
51+
// Verify we received the RuntimeException
52+
testHelper.ensureErrorReceived { event ->
53+
event.exceptions?.any { ex -> ex.type == "RuntimeException" && ex.value == "Some error!" } ==
54+
true
55+
}
56+
57+
// Verify we received the detailed event with fingerprint
58+
testHelper.ensureErrorReceived { event ->
59+
event.message?.message == "Detailed event" &&
60+
event.fingerprints?.contains("NewClientDebug") == true &&
61+
event.level?.name == "DEBUG"
62+
}
63+
64+
// Verify we received transaction events
65+
testHelper.ensureTransactionReceived { transaction, _ ->
66+
transaction.transaction == "transaction name" &&
67+
transaction.spans?.any { span -> span.op == "child" } == true
68+
}
69+
70+
// Verify we received the loop messages (should be 10 of them)
71+
var loopMessageCount = 0
72+
try {
73+
for (i in 0..9) {
74+
testHelper.ensureErrorReceived { event ->
75+
val matches =
76+
event.message?.message?.contains("items we'll wait to flush to Sentry!") == true
77+
if (matches) loopMessageCount++
78+
matches
79+
}
80+
}
81+
} catch (e: Exception) {
82+
// Some loop messages might be missing, but we should have at least some
83+
}
84+
85+
assertTrue(
86+
"Should receive at least 5 loop messages, got $loopMessageCount",
87+
loopMessageCount >= 5,
88+
)
89+
90+
// Verify we have breadcrumbs
91+
testHelper.ensureErrorReceived { event ->
92+
event.breadcrumbs?.any { breadcrumb ->
93+
breadcrumb.message?.contains("Processed by") == true
94+
} == true
95+
}
96+
}
97+
}

sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import io.sentry.SentryOptions
1111
import io.sentry.protocol.SentrySpan
1212
import io.sentry.protocol.SentryTransaction
1313
import io.sentry.systemtest.graphql.GraphqlTestClient
14+
import java.io.File
1415
import java.io.PrintWriter
1516
import kotlin.test.assertEquals
1617
import kotlin.test.assertFalse
@@ -22,6 +23,7 @@ class TestHelper(backendUrl: String) {
2223
val graphqlClient: GraphqlTestClient
2324
val sentryClient: SentryMockServerClient
2425
val jsonSerializer: JsonSerializer
26+
val dsn = "http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0"
2527

2628
var envelopeCounts: EnvelopeCounts? = null
2729

@@ -42,8 +44,7 @@ class TestHelper(backendUrl: String) {
4244
assertTrue(envelopeCountsAfter!!.envelopes!! > envelopeCounts!!.envelopes!!)
4345
}
4446

45-
fun ensureEnvelopeReceived(callback: ((String) -> Boolean)) {
46-
Thread.sleep(10000)
47+
fun ensureEnvelopeReceived(retryCount: Int = 1, callback: ((String) -> Boolean)) {
4748
val envelopes = sentryClient.getEnvelopes()
4849
assertNotNull(envelopes.envelopes)
4950
envelopes.envelopes.forEach { envelopeString ->
@@ -52,7 +53,12 @@ class TestHelper(backendUrl: String) {
5253
return
5354
}
5455
}
55-
throw RuntimeException("Unable to find matching envelope received by relay")
56+
if (retryCount <= 0) {
57+
throw RuntimeException("Unable to find matching envelope received by relay")
58+
} else {
59+
Thread.sleep(10000)
60+
ensureEnvelopeReceived(retryCount - 1, callback)
61+
}
5662
}
5763

5864
fun ensureNoEnvelopeReceived(callback: ((String) -> Boolean)) {
@@ -268,4 +274,26 @@ class TestHelper(backendUrl: String) {
268274

269275
return true
270276
}
277+
278+
fun findJar(prefix: String, inDir: String = "build/libs"): File {
279+
val buildDir = File(inDir)
280+
val jarFiles =
281+
buildDir.listFiles { _, name -> name.startsWith(prefix) && name.endsWith(".jar") }?.toList()
282+
?: emptyList()
283+
284+
if (jarFiles.isEmpty()) {
285+
throw AssertionError("No JAR found in ${buildDir.absolutePath}")
286+
}
287+
288+
return jarFiles.maxOf { it }
289+
}
290+
291+
fun launch(jar: File, env: Map<String, String>): Process {
292+
val processBuilder =
293+
ProcessBuilder("java", "-jar", jar.absolutePath).inheritIO() // forward i/o to current process
294+
295+
processBuilder.environment().putAll(env)
296+
297+
return processBuilder.start()
298+
}
271299
}

test/system-test-run-all.sh

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)