diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index ffa539c044..43f69a6988 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -78,6 +78,9 @@ jobs: - sample: "sentry-samples-spring-boot-4-opentelemetry" agent: "true" agent-auto-init: "false" + - sample: "sentry-samples-spring-7" + agent: "false" + agent-auto-init: "true" - sample: "sentry-samples-spring-jakarta" agent: "false" agent-auto-init: "true" diff --git a/sentry-samples/sentry-samples-spring-7/build.gradle.kts b/sentry-samples/sentry-samples-spring-7/build.gradle.kts index f156512474..1d31ff91d0 100644 --- a/sentry-samples/sentry-samples-spring-7/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-7/build.gradle.kts @@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.springframework.boot.gradle.plugin.SpringBootPlugin plugins { + application alias(libs.plugins.springboot4) apply false alias(libs.plugins.spring.dependency.management) alias(libs.plugins.kotlin.jvm) @@ -11,6 +12,11 @@ plugins { alias(libs.plugins.gretty) } +application { mainClass.set("io.sentry.samples.spring7.Main") } + +// Ensure WAR is up to date before run task +tasks.named("run") { dependsOn(tasks.named("war")) } + group = "io.sentry.sample.spring-7" version = "0.0.1-SNAPSHOT" @@ -37,13 +43,17 @@ dependencies { implementation(libs.logback.classic) implementation(libs.servlet.jakarta.api) implementation(libs.slf4j2.api) + + implementation(libs.tomcat.catalina.jakarta) + implementation(libs.tomcat.embed.jasper.jakarta) + + testImplementation(projects.sentrySystemTestSupport) + testImplementation(libs.kotlin.test.junit) testImplementation(libs.springboot.starter.test) { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } } -tasks.withType().configureEach { useJUnitPlatform() } - tasks.withType().configureEach { kotlin { explicitApi() @@ -55,3 +65,26 @@ tasks.withType().configureEach { compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 } } + +configure { test { java.srcDir("src/test/java") } } + +tasks.register("systemTest").configure { + group = "verification" + description = "Runs the System tests" + + outputs.upToDateWhen { false } + + maxParallelForks = 1 + + // Cap JVM args per test + minHeapSize = "128m" + maxHeapSize = "1g" + + 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-7/src/main/java/io/sentry/samples/spring7/Main.java b/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/Main.java new file mode 100644 index 0000000000..805e08c641 --- /dev/null +++ b/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/Main.java @@ -0,0 +1,32 @@ +package io.sentry.samples.spring7; + +import java.io.File; +import java.io.IOException; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.startup.Tomcat; + +public class Main { + + public static void main(String[] args) throws LifecycleException, IOException { + File webappsDirectory = new File("./tomcat.8080/webapps"); + if (!webappsDirectory.exists()) { + boolean didCreateDirectories = webappsDirectory.mkdirs(); + if (!didCreateDirectories) { + throw new RuntimeException( + "Failed to create directory required by Tomcat: " + webappsDirectory.getAbsolutePath()); + } + } + + String pathToWar = "./build/libs"; + String warName = "sentry-samples-spring-7-0.0.1-SNAPSHOT"; + File war = new File(pathToWar + "/" + warName + ".war"); + + Tomcat tomcat = new Tomcat(); + tomcat.setPort(8080); + tomcat.getConnector(); + + tomcat.addWebapp("/" + warName, war.getCanonicalPath()); + tomcat.start(); + tomcat.getServer().await(); + } +} diff --git a/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/Person.java b/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/Person.java index 784291f1c0..b2ff7fff4a 100644 --- a/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/Person.java +++ b/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/Person.java @@ -1,10 +1,15 @@ package io.sentry.samples.spring7.web; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class Person { private final String firstName; private final String lastName; - public Person(String firstName, String lastName) { + @JsonCreator + public Person( + @JsonProperty("firstName") String firstName, @JsonProperty("lastName") String lastName) { this.firstName = firstName; this.lastName = lastName; } diff --git a/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/PersonController.java b/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/PersonController.java index 5baf24acc1..a9a413fd5f 100644 --- a/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/PersonController.java +++ b/sentry-samples/sentry-samples-spring-7/src/main/java/io/sentry/samples/spring7/web/PersonController.java @@ -22,7 +22,7 @@ public PersonController(PersonService personService) { } @GetMapping("{id}") - Person person(@PathVariable Long id) { + Person person(@PathVariable("id") Long id) { Sentry.logger().warn("warn Sentry logging"); Sentry.logger().error("error Sentry logging"); Sentry.logger().info("hello %s %s", "there", "world!"); diff --git a/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/DummyTest.kt b/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/DummyTest.kt new file mode 100644 index 0000000000..6f762b7e45 --- /dev/null +++ b/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/DummyTest.kt @@ -0,0 +1,12 @@ +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-7/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt new file mode 100644 index 0000000000..628c27f4c6 --- /dev/null +++ b/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -0,0 +1,46 @@ +package io.sentry.systemtest + +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/sentry-samples-spring-7-0.0.1-SNAPSHOT") + testHelper.reset() + } + + @Test + fun `get person fails`() { + val restClient = testHelper.restClient + restClient.getPerson(11L) + assertEquals(500, restClient.lastKnownStatusCode) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + testHelper.doesTransactionHaveOp(transaction, "http.server") + } + + 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!") + } + } + + @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) + } +} diff --git a/test/system-test-runner.py b/test/system-test-runner.py index c25c79dcb9..182bf164f4 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -713,6 +713,7 @@ def get_available_modules(self) -> List[ModuleConfig]: """Get list of all available test modules.""" return [ ModuleConfig("sentry-samples-spring", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-7", "false", "true", "false"), ModuleConfig("sentry-samples-spring-jakarta", "false", "true", "false"), ModuleConfig("sentry-samples-spring-boot", "false", "true", "false"), ModuleConfig("sentry-samples-spring-boot-opentelemetry-noagent", "false", "true", "false"),