/actuator/prometheus missing in #SpringbootTest - java

I'm using springbooot 2.4.0 and I added the following dependencies for enabling prometheus metrics:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
then in my application.properties I have the following properties
management.endpoints.web.exposure.include=*
management.metrics.enable.all=true
I'm trying to run a simple integration test to see my custom metrics appearing at /actuator/prometheus endpoint. Below the code
package com.example.demo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {
#LocalServerPort
private int port;
private String baseUrl;
#BeforeEach
public void setup() {
baseUrl = "http://localhost:" + port;
}
#Test
public void metricsEndpoint() throws Exception {
given().when().get(baseUrl + "/demo/actuator/prometheus")
.then()
.statusCode(200);
}
}
The error I get here is
java.lang.AssertionError: 1 expectation failed.
Expected status code <200> but was <404>.
while if I repeat the same request for any other endpoint provided by springboot actuator I correctly geth the response, for example I tried /actuator/health, /actuator/info, /actuator/metrics etc..
This happens only during integration tests with #Springboot annotation and this is strange because if I run my application and make a request with postman to the address localhost:8080/actuator/prometheus I correctly get a response.
It is like the prometheus registry is not loaded during tests.
Can anyone help?
Thanks in advance.
EDIT: the solution is the one suggested by Johannes Klug. Adding the annotation #AutoConfigureMetrics solved my problem

I faced the same issue. After some tracing through spring-context ConditionEvaluator, I found that the newly introduced #ConditionalOnEnabledMetricsExport("prometheus") condition on PrometheusMetricsExportAutoConfiguration prevented the endpoint from loading.
This is intended behavior due to https://github.com/spring-projects/spring-boot/pull/21658 and impacts spring-boot 2.4.x
Fix:
add #AutoConfigureMetrics to your test
#AutoConfigureMetrics
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {

It's happening to me too, with Spring Boot 2.4.1
The only thing that I can think of, is that the endpoints that are included in #SpringBootTest are using the #Endpoint annotation.
For example:
#Endpoint(id = "metrics")
public class MetricsEndpoint {}
#Endpoint(id = "loggers")
public class LoggersEndpoint {}
And the prometheus endpoint is using the #WebEndpoint annotation
#WebEndpoint(id = "prometheus")
public class PrometheusScrapeEndpoint {}
I didn't find if this is how it should work, or is a probable bug

I see in your test, you are appending "demo" before actual actuator path.
given().when().get(baseUrl + "/demo/actuator/prometheus")
.then()
.statusCode(200);
The correct url should be baseUrl+"/actuator/prometheus".

Since Spring Boot 3.0.0 #AutoConfigureMetrics has been deprecated and will be removed in 3.2.0, it is recommended to use #AutoConfigureObservability instead

Related

How to use stable RestHighLevelClient with Elasticsearch?

I have searched for so many posts but I couldn't find a proper way to use Elastic Search with spring boot application because I am totally new to elastic search.
My only dependency is:
org.springframework.boot
spring-boot-starter-data-elasticsearch
2.7.3
My config class is:
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
#Configuration
#EnableElasticsearchRepositories(basePackages = "com.backend.repository.elasticsearchrepository")
#ComponentScan(basePackages = {"com.backend.model.elasticsearchmodel"})
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {
#Value("${spring.elasticsearch.url}")
public String elasticsearchUrl;
#Value("${spring.elasticsearch.username}")
public String username;
#Value("${spring.elasticsearch.password}")
public String password;
#Bean
#Override
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration config = ClientConfiguration.builder()
.connectedTo(elasticsearchUrl)
.withBasicAuth(username, password)
.build();
return RestClients.create(config).rest();
}
}
Here RestHighLevelClient is shown as deprecated. And my repository class is:
package com.backend.repository.elasticsearchrepository;
import com.backend.model.elasticsearchmodel.EsOffice;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.UUID;
public interface ESOfficeRepository extends ElasticsearchRepository<EsOffice, UUID> {
}
When I call the methods of this repository then it works fine but while storing the data it is returning error message even if it adds the data successfully.
2022-10-15 00:00:15.608 ERROR 51607 --- [nio-8080-exec-2] c.a.a.exception.GlobalExceptionHandler : Unable to parse response body for Response{requestLine=POST /office/_doc?timeout=1m HTTP/1.1, host=http://localhost:9200, response=HTTP/1.1 201 Created}; nested exception is java.lang.RuntimeException: Unable to parse response body for Response{requestLine=POST /office/_doc?timeout=1m HTTP/1.1, host=http://localhost:9200, response=HTTP/1.1 201 Created}
Which POM dependency + what kind of repository should I use and How can I configure it in my config file ? I need these 3 that compatible with each other ?
Spring Data Elasticsearch 4.4 (which is pulled in by Spring Boot 2.7.3) is build with the Elasticsearch libraries in version 7.17, this is problematic when running against an Elasticsearch cluster in version 8. Youhave basically two options:
Downgrade your cluster to version 7.17.6 (the latest 7.17 currently available) i f this is possible.
You can try and see if setting the compatibility headers (see the Spring Data Elasticsearch documentation section 5.3.1 for more info). This should work, but I encountered cases where the response from the cluster still wasn't readable with a 7.17 client. - I had issues opened with Elasticsearch and they were resolved, but there still might be hidden traps.

Spring Boot Integration test mock external dependency

I'm trying to create integration tests for my Spring Boot app. The idea is to launch an embedded postgres db and run http calls with TestRestTemplate to my controllers.
The problem is my project has a dependency we use for redis queues.
<dependency>
<groupId>com.github.sonus21</groupId>
<artifactId>rqueue-spring-boot-starter</artifactId>
<version>2.9.0-RELEASE</version>
</dependency>
I've tried to mock out the dependencies and most of them work, but with this one it complains I guess because it is a #Configuration not a #Component:
Dependency config class:
#Configuration
#AutoConfigureAfter({RedisAutoConfiguration.class})
#ComponentScan({"com.github.sonus21.rqueue.web", "com.github.sonus21.rqueue.dao"})
public class RqueueListenerAutoConfig extends RqueueListenerBaseConfig {
public RqueueListenerAutoConfig() {
}
...
}
My test config class
#TestConfiguration
public class TestRestTemplateConfig {
#Bean
#Primary
#Order(Ordered.HIGHEST_PRECEDENCE)
public RqueueListenerAutoConfig rqueueListenerAutoConfig() {
return Mockito.mock(RqueueListenerAutoConfig.class);
}
....
}
I've tried with #AutoConfigureOrder(1) at my config class but the original RqueueListenerAutoConfig launches before anything and my mocked beans haven't been declared yet.
To be honest mocking every service on that dependency is a pain, but I haven't figured out a way to mock the whole dependency with a single configuration. I tried not loading the dependency when I'm on the test profile but since it runs spring context my code needs it.
My test class has the following config:
#SpringBootTest
#Import(TestRestTemplateConfig.class)
#ActiveProfiles("test")
public class TestClass {
...
}
Any clues?
Thanks.
Try
#EnableAutoConfiguration(exclude=RqueueListenerAutoConfig.class)

SpringBoot #WebMvcTest security issue

I have a spring rest mvc controller which has the url "/public/rest/vehicle/get". In my security configuration, I have defined that any requests for /public/rest should not require authentication.
http.
csrf().disable()
.authorizeRequests()
.antMatchers("/home/**", "/**", "/css/**", "/js/**", "/fonts/**", "/images/**", "/public/rest/**","/login*","/signin/**","/signup/**").permitAll()
.antMatchers("/property/**").authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and().httpBasic().disable();
This configuration works fine when I start my application and submit request using browser or any other mean.
Now, I have a test class which looks like this,
#RunWith(SpringRunner.class)
#WebMvcTest(VehicleController.class)
public class VehicleControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private VehicleService vehicleService;
#Test
public void getVehicle() throws Exception {
given(this.vehicleService.get(0)).
willReturn(new VehicleEquipmentDTO());
this.mockMvc.perform(get("/public/rest/vehicle/get").param("id","0"))
.andDo(print())
.andExpect(status().isOk());//.andExpect(content().string("Honda Civic"));
}}
Now, when I run this test, it says
java.lang.AssertionError: Status
Expected :200
Actual :401
When I print the request response, I see it is complaining because of security. "Error message = Full authentication is required to access this resource"
Any ideas why it is not working with the security config that I have, and what is the work around to force it to use the correct configurations? Thanks in advance
Finally found the reason. Since WebMvcTest is only sliced testing, it would not take the security configurations. The work around is to explicitly import it like,
#Import(WebSecurityConfig.class)
I had the same issue and after searching for a while, I found the following solution.
Because, you have Spring Security enabled in your application, along with other annotations, you can specify the secure=false parameter in #AutoConfigureMockMvc annotation like this:
#AutoConfigureMockMvc(secure=false)
public class YourControllerTest {
//All your test methods here
}
Explanation:
To disable the Spring Security auto-configuration, we can use the MockMvc instance to disable security with #AutoConfigureMockMvc(secure=false)
This solved me the same issue
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ClientResourceControllerTest {

Spring Boot - how to include REST endpoint from dependency?

I am new to Spring / Spring Boot, so please pardon if what I am asking is trivial.
I have created Spring Boot application which exposes the REST endpoint:
package com.atomic.contentguard;
...
#Controller
#RequestMapping("/rest")
public class AcgController {
#RequestMapping(value="/acg-status",method=RequestMethod.GET)
#ResponseBody
public String getStatus(){
return "Hi there!";
}
}
It all works fine when you run it as standalone Spring Boot application, the endpoint is testable by going to http://localhost:8080/rest/acg-status.
What I want to achieve is to "bring it" into another application, which would be including my application as a dependency in the pom.xml, expecting this REST endpoint to show up in it.
What I've done so far is included it in another project pom.xml as:
</dependencies>
...
<dependency>
<groupId>com.atomic</groupId>
<artifactId>contentguard</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
And then included it in that other application #ComponentScan section of config file:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.atomic.contentguard"})
public class EnvInfoWebConfig extends WebMvcConfigurerAdapter {
}
It does not however show up when you run target application:
No mapping found for HTTP request with URI [/other-application-context/rest/acg-status] in DispatcherServlet with name 'envinfo-dispatcher'
What am I missing / doing wrong?
You can do this simply by using the spring boot Application Launcher class in your main project as below (You don't need WebMvcConfigurerAdapter class):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan(basePackages = { "com.atomic.contentguard"})
public class AcgLauncher extends SpringBootServletInitializer {
//This method is required to launch the ACG application
public static void main(String[] args) {
// Launch Trainserv Application
SpringApplication.run(AcgLauncher.class, args);
}
}
Spring Boot uses this class during the server startup and scans the specified packages for all spring components (controllers, services, components).

Unable to use #DataJpaTest if Spring security is on classpath

I am trying to update my test cases to use the #DataJpaTest. However, I am encountering some issues that appear to be related to Spring Security. The following is an example of the test class.
#RunWith(SpringRunner.class)
#DataJpaTest
public class ExampleRepositoryTest {
#Rule
public final ExpectedException exception = ExpectedException.none();
#Inject
private ExampleRepository repository;
#Test
public void test() throws Exception {
...
}
I keep getting the error java.lang.IllegalStateException: Failed to load ApplicationContext due to the missing bean org.springframework.security.config.annotation.ObjectPostProcessor.
The project is a RESTful application with Spring security. The original test case created a full Spring Boot context using #SpringBootTest. The #DataJpaTest is supposed to help me test the JPA slice, which is exactly what I want.
Any help would be greatly appreciated. What am I missing?
I got the same error.
for my case, I have added #SpringBootApplication and #EnableResourceServer on the same class.
when I move #EnableResourceServer to another configuration class, the error is gone.

Categories

Resources