diff --git a/actuator-webmvc-mgmt-port/README.adoc b/actuator-webmvc-mgmt-port/README.adoc new file mode 100644 index 00000000..d1d4136a --- /dev/null +++ b/actuator-webmvc-mgmt-port/README.adoc @@ -0,0 +1 @@ +Tests the Spring Boot Actuator on WebMVC. diff --git a/actuator-webmvc-mgmt-port/build.gradle b/actuator-webmvc-mgmt-port/build.gradle new file mode 100644 index 00000000..271e4dd6 --- /dev/null +++ b/actuator-webmvc-mgmt-port/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' + id 'org.springframework.boot' + id 'org.springframework.aot.smoke-test' + id 'org.graalvm.buildtools.native' +} + +dependencies { + implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("io.micrometer:micrometer-registry-prometheus") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + + aotTestImplementation(project(":aot-smoke-test-support")) +} + +aotSmokeTest { + webApplication = true +} diff --git a/actuator-webmvc-mgmt-port/src/aotTest/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplicationAotTests.java b/actuator-webmvc-mgmt-port/src/aotTest/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplicationAotTests.java new file mode 100644 index 00000000..01741aa2 --- /dev/null +++ b/actuator-webmvc-mgmt-port/src/aotTest/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplicationAotTests.java @@ -0,0 +1,82 @@ +package com.example.actuator.webmvc.mgmtport; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.aot.smoketest.support.Output; +import org.springframework.aot.smoketest.support.junit.AotSmokeTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.assertj.core.api.Assertions.assertThat; + +@AotSmokeTest +class ActuatorWebMvcMgmtPortApplicationAotTests { + + private static final Pattern MANAGEMENT_PORT_REGEX = Pattern.compile("^Management port: (\\d+)$"); + + private static WebTestClient client; + + @BeforeAll + static void beforeAll() { + client = buildManagementClient(); + } + + @Test + void shouldContainLinks() { + client.get().uri("/actuator").exchange().expectStatus().isOk().expectBody().jsonPath("$._links.self.templated") + .isEqualTo(false).jsonPath("$._links.env-toMatch.templated").isEqualTo(true); + } + + @Test + void shouldHaveReadiness() { + client.get().uri("/actuator/health/readiness").exchange().expectStatus().isOk().expectBody() + .jsonPath("$.status").isEqualTo("UP"); + } + + @Test + void shouldHaveEnvInfoProperties() { + client.get().uri("/actuator/info").exchange().expectStatus().isOk().expectBody().jsonPath("$.app.hello") + .isEqualTo("world"); + } + + @Test + void shouldHaveJavaInfoProperties() { + client.get().uri("/actuator/info").exchange().expectStatus().isOk().expectBody().jsonPath("$.java.version") + .isNotEmpty(); + } + + @Test + void shouldHaveOsInfoProperties() { + client.get().uri("/actuator/info").exchange().expectStatus().isOk().expectBody().jsonPath("$.os.name") + .isNotEmpty(); + } + + @Test + void shouldHaveMetrics() { + client.get().uri("/actuator/metrics/jvm.classes.loaded").exchange().expectStatus().isOk().expectBody() + .jsonPath("$.measurements.[0].value").isNotEmpty(); + } + + @Test + void shouldHavePrometheusMetrics() { + client.get().uri("/actuator/prometheus").exchange().expectStatus().isOk().expectBody() + .consumeWith((result) -> assertThat(new String(result.getResponseBodyContent())) + .contains("jvm_classes_loaded_classes ")); + + } + + private static WebTestClient buildManagementClient() { + for (String line : Output.current().lines()) { + Matcher matcher = MANAGEMENT_PORT_REGEX.matcher(line); + if (matcher.matches()) { + int port = Integer.parseInt(matcher.group(1)); + return WebTestClient.bindToServer().baseUrl("http://localhost:%d/".formatted(port)).build(); + } + } + throw new IllegalStateException("No management port found in output"); + } + +} diff --git a/actuator-webmvc-mgmt-port/src/main/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplication.java b/actuator-webmvc-mgmt-port/src/main/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplication.java new file mode 100644 index 00000000..d37d4b8b --- /dev/null +++ b/actuator-webmvc-mgmt-port/src/main/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplication.java @@ -0,0 +1,24 @@ +package com.example.actuator.webmvc.mgmtport; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.context.event.EventListener; + +@SpringBootApplication +public class ActuatorWebMvcMgmtPortApplication { + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(ActuatorWebMvcMgmtPortApplication.class, args); + Thread.currentThread().join(); // To be able to measure memory consumption + } + + @EventListener + public void onAppEvent(WebServerInitializedEvent e) { + // This is needed to get the management port in the AOT tests + if ("management".equals(e.getApplicationContext().getServerNamespace())) { + System.out.printf("Management port: %d%n", e.getWebServer().getPort()); + } + } + +} diff --git a/actuator-webmvc-mgmt-port/src/main/resources/application.properties b/actuator-webmvc-mgmt-port/src/main/resources/application.properties new file mode 100644 index 00000000..956f2750 --- /dev/null +++ b/actuator-webmvc-mgmt-port/src/main/resources/application.properties @@ -0,0 +1,9 @@ +management.server.port=0 +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always +management.endpoint.health.show-components=always +management.endpoint.health.probes.enabled=true +management.info.env.enabled=true +management.info.java.enabled=true +management.info.os.enabled=true +info.app.hello=world diff --git a/actuator-webmvc-mgmt-port/src/test/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplicationTests.java b/actuator-webmvc-mgmt-port/src/test/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplicationTests.java new file mode 100644 index 00000000..80750c9d --- /dev/null +++ b/actuator-webmvc-mgmt-port/src/test/java/com/example/actuator/webmvc/mgmtport/ActuatorWebMvcMgmtPortApplicationTests.java @@ -0,0 +1,14 @@ +package com.example.actuator.webmvc.mgmtport; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ActuatorWebMvcMgmtPortApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/ci/smoke-tests.yml b/ci/smoke-tests.yml index 7a873712..bae98bee 100644 --- a/ci/smoke-tests.yml +++ b/ci/smoke-tests.yml @@ -2,6 +2,7 @@ smoke_tests: - actuator-webflux - actuator-webflux-mgmt-port - actuator-webmvc + - actuator-webmvc-mgmt-port - aspect - async - batch diff --git a/settings.gradle b/settings.gradle index f2d806ef..dfda7213 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,7 @@ include "aot-smoke-test-third-party-hints" include "actuator-webflux" include "actuator-webflux-mgmt-port" include "actuator-webmvc" +include "actuator-webmvc-mgmt-port" include "aspect" include "async" include "batch"