Add an actuator WebFlux smoke test
Closes gh-28
This commit is contained in:
1
boot/actuator-webflux/README.adoc
Normal file
1
boot/actuator-webflux/README.adoc
Normal file
@@ -0,0 +1 @@
|
||||
Tests the Spring Boot Actuator on WebFlux.
|
||||
23
boot/actuator-webflux/build.gradle
Normal file
23
boot/actuator-webflux/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "org.springframework.boot"
|
||||
id "org.springframework.cr.smoke-test"
|
||||
}
|
||||
|
||||
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-webflux")
|
||||
implementation("org.crac:crac:$cracVersion")
|
||||
implementation(project(":cr-listener"))
|
||||
|
||||
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
|
||||
appTestImplementation(project(":cr-smoke-test-support"))
|
||||
}
|
||||
|
||||
crSmokeTest {
|
||||
webApplication = true
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.example.actuator.webmvc;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cr.smoketest.support.junit.ApplicationTest;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ApplicationTest
|
||||
class ActuatorWebFluxApplicationAotTests {
|
||||
|
||||
@Test
|
||||
void shouldContainLinks(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$._links.self.templated")
|
||||
.isEqualTo(false)
|
||||
.jsonPath("$._links.env-toMatch.templated")
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveCustomHealthIndicator(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/health")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.status")
|
||||
.isEqualTo("UP")
|
||||
.jsonPath("$.components.custom.status")
|
||||
.isEqualTo("UP")
|
||||
.jsonPath("$.components.custom.details.hello")
|
||||
.isEqualTo("world");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveAnotherCustomHealthIndicator(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/health")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.components.anotherCustom.status")
|
||||
.isEqualTo("UP");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveCompositeHealth(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/health")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.components.composite.status")
|
||||
.isEqualTo("UP")
|
||||
.jsonPath("$.components.composite.components.another-custom.status")
|
||||
.isEqualTo("UP")
|
||||
.jsonPath("$.components.composite.components.custom.status")
|
||||
.isEqualTo("UP");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveCustomEndpoint(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/custom")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.consumeWith((result) -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("custom-read"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveCustomWebEndpoint(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/customWeb")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(299)
|
||||
.expectBody()
|
||||
.consumeWith(
|
||||
(result) -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("customWeb-read"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveReadiness(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/health/readiness")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.status")
|
||||
.isEqualTo("UP");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveEnvInfoProperties(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/info")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.app.hello")
|
||||
.isEqualTo("world");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveJavaInfoProperties(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/info")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.java.version")
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveOsInfoProperties(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/info")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.os.name")
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveMetrics(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/metrics/jvm.classes.loaded")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.measurements.[0].value")
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void prometheusWorks(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/prometheus")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.consumeWith((result) -> assertThat(new String(result.getResponseBodyContent()))
|
||||
// Check custom timer
|
||||
.contains("custom_timer_seconds_max 5.0")
|
||||
.contains("custom_timer_seconds_count 1.0")
|
||||
.contains("custom_timer_seconds_sum 5.0")
|
||||
// Check JVM metric
|
||||
.contains("# TYPE jvm_threads_peak_threads gauge"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveLoggers(WebTestClient client) {
|
||||
client.get()
|
||||
.uri("/actuator/loggers")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.levels")
|
||||
.isNotEmpty()
|
||||
.jsonPath("$.loggers.['ROOT']")
|
||||
.isNotEmpty()
|
||||
.jsonPath("$.loggers.['_org.springframework']")
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.example.actuator.webmvc;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.example.actuator.webmvc.health.AnotherCustomHealthIndicator;
|
||||
import com.example.actuator.webmvc.health.CustomHealthIndicator;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthContributor;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ActuatorWebfluxApplication {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
SpringApplication.run(ActuatorWebfluxApplication.class, args);
|
||||
Thread.currentThread().join(); // To be able to measure memory consumption
|
||||
}
|
||||
|
||||
@Bean("composite")
|
||||
CompositeHealthContributor compositeHealthContributor(CustomHealthIndicator customHealthIndicator,
|
||||
AnotherCustomHealthIndicator anotherCustomHealthIndicator) {
|
||||
return CompositeHealthContributor
|
||||
.fromMap(Map.of("custom", customHealthIndicator, "another-custom", anotherCustomHealthIndicator));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.example.actuator.webmvc;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Endpoint(id = "custom")
|
||||
class CustomEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
String read() {
|
||||
return "custom-read";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.example.actuator.webmvc;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@WebEndpoint(id = "customWeb")
|
||||
class CustomWebEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
WebEndpointResponse<String> read() {
|
||||
return new WebEndpointResponse<>("customWeb-read", 299);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.example.actuator.webmvc;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
class RegisterCustomTimer implements CommandLineRunner {
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
|
||||
RegisterCustomTimer(MeterRegistry meterRegistry) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
this.meterRegistry.timer("custom.timer").record(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.example.actuator.webmvc.health;
|
||||
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AnotherCustomHealthIndicator implements HealthIndicator {
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
return Health.up().build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.example.actuator.webmvc.health;
|
||||
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CustomHealthIndicator implements HealthIndicator {
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
return Health.up().withDetail("hello", "world").build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
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
|
||||
Reference in New Issue
Block a user