Wiremock does not receive any requests - spring

I am writing an integrationtest for a resource (Jax-rs with resteasy) which invokes a thirdparty request to the Trustpilot Api to get a review link for a product.
In my tests I wanted to mock this call by using Wiremock and invoke the request by using resteasy Dispatcher.
The code below shows my Setup (names etc are changed).
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyResourceTestContext.class })
public class MyResourceIntegrationTest {
#Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort().dynamicHttpsPort());
#Autowired
private MyResource myResource;
private Dispatcher dispatcher;
private MockHttpResponse response;
#Before
public void setUp() throws Exception {
dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(myResource);
response = new MockHttpResponse();
}
#Test
public void testResourceShouldReturnRedirectToTrustpilotreview() throws Exception {
URI apiRequestURI = UriBuilder
.fromUri("https://api.trustpilot.com/v1/private/product-reviews/business-units"
+ "/{businessUnitId}}/invitation-links")
.build(BUSINESS_UNIT_ID);
URI expectedRedirectURI = UriBuilder.fromUri("https://products.trustpilot.com/#evaluate/{reviewLinkId}")
.build(REVIEW_LINK_ID);
stubFor(post(apiRequestURI.toString())
.willReturn(okJson("{\"reviewLinkId\":\"" + REVIEW_LINK_ID + "\","
+ "\"reviewUrl\":\"" + expectedRedirectURI.toString() + "\"}")));
MockHttpRequest request = MockHttpRequest.get("/myResource");
dispatcher.invoke(request, response);
WireMock.verify(1, postRequestedFor(urlEqualTo(apiRequestURI.toString())));
Assert.assertEquals(HttpStatus.TEMPORARY_REDIRECT.value(), response.getStatus());
Assert.assertEquals(expectedRedirectURI.toString(), response.getOutputHeaders().getFirst("Location"));
}
But what I'm getting is (with my actual bid in the url):
com.github.tomakehurst.wiremock.client.VerificationException: Expected at least one request matching: {
"url" : "https://api.trustpilot.com/v1/private/product-reviews/business-units/<bid>/invitation-links",
"method" : "POST"
}
Requests received: [ ]
Stepping into the request with the Debugger I can say this request and other requests are actually executed successfully. But they are not recognized/mocked by Wiremock.
Is there something wrong with my Wiremock setup? Any Ideas? Thank you!

There are a couple of reasons why this isn't sending anything to WireMock at the moment:
WireMock only accepts relative URLs when stubbing so passing the full URL including protocol and domain won't work. Try something equivalent to stubFor(post(urlPathEqualTo("/v1/private/product-reviews/business-units/<bid>/invitation-links") instead.
There doesn't seem to be any way for your class under test to know which host/port WireMock is running on and no forward proxying set up so the code is presumably still trying to call the real Trustpilot API.
To solve 2. you have a couple of options:
Set up your class/code under test to use localhost as the destination host and the result of wireMockRule.port() as the port number, or wireMockRule.httpsPort() if you're on HTTPS.
Use the new auto JVM proxy setup feature to proxy your app's calls through WireMock. This assumes you're on WireMock 2.31.0+ and that your app's HTTP client respects the JVM proxy system properties.

Related

Is there support for multiple feign.Client beans within Spring

Some context
The project is a spring boot application (v 2.3.12) using spring-cloud-openfeign-core (v 2.2.8) to make REST requests.
The service has 2 clients
one that needs to make REST requests using a proxy
one that needs to make REST request without a proxy
I'm unable to get both client to work simultaneously, i.e. have requests work for both proxied and non-proxied resources.
Is it possible to use different configuration files to support this, I have tried something like this
Configuration class X
#bean
public Client client1() {
return new Client.Default(null, null);
}
Configuration class Y
#bean
public Client client2() {
return new Client.Proxied(null, null,
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("10.0.0.1", 8080)));
}
Feign interface code looks something like this
#Service
#FeignClient(name = "service1", url = "internal-url", configuration = X.class)
#Headers("Content-Type: application/json")
public interface serviceClient1 {
#PostMapping(value = "/v1/path}")
Response1 update(#RequestBody Request1 request1);
}
#Service
#FeignClient(name = "service2", url = "external-url", configuration = Y.class)
#Headers("Content-Type: application/json")
public interface serviceClient2 {
#PostMapping(value = "/v1/path}")
Response2 update(#RequestBody Request2 request2);
}
It seems only one of the client beans is used when making requests.

What's the best usage for mockOIdcLogin() for WebTestClient?

I'm currently working on a senior project and we decided to use Spring Webflux for our backend and Google as our OAuth2.0 provider. I am currently trying to run some integration tests using Spock and Groovy on some endpoints that are secured behind OAuth2.0 authentication. The endpoint does not use the Authentication principal for anything, is just not supposed to be accessed by someone who isn't authenticated. However, reading the Spring documentation and I came across the method for a webTestClient to use a mock open id connect login in which I might not need to do mock the entire OAuth2 process, however, this is giving me a HTTP 302 status
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = "spring.main.web-application-type=reactive")
class UserControllerITSpec extends Specification {
#Autowired
ReactiveWebApplicationContext context
#Autowired
ApplicationContext applicationContext
#Autowired
WebTestClient client
#Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig()
.port(8077))
def setup() {
client = WebTestClient.bindToApplicationContext(context)
.configureClient()
.build()
}
def "getAllUsers is successful"() {
given: "a request"
def request = client.mutateWith(mockOidcLogin()).get().uri("/api/v2/users/getAllUsers")
stubFor(post("/graphql/")
.withHeader("Authorization", equalTo("F9v4MUqdQuWAh3Wqxe11mteqPfPedUqp78VaQNJt8DSt"))
.withHeader("content-type", equalTo("application/json"))
.withHeader("accept", equalTo("application/json"))
.withRequestBody(equalTo("""{
"query": "query { list_UserItems { _UserItems { _id email displayName appointments } } }",
"variables": null,
"operationName": null
}"""))
.willReturn(aResponse()
.withStatus(200)
.withBodyFile("vendiaResponses/getAllUsersResponse.json")))
stubFor(get("/oauth2/authorization/wiremock")
.willReturn(status(200)))
when: "the request is sent"
def response = request.exchange()
then: "an OK status is returned"
response.expectStatus()
.isOk()
}
}
Is my understanding of the mutateWith(mockOidcLogin()) method incorrect?
I'd consider using webEnvironment = SpringBootTest.WebEnvironment.MOCK
Are you sure about the Authentication implementation you have at runtime? (you can have Authentication auth auto-magically injected as #Controller method parameter and inspect it with your favorite debugger or log its class)
Isn't there anything of help in those samples: https://github.com/ch4mpy/spring-addons/tree/master/samples (look at those containing webflux in their name and focus on test classes ending with ItegrationTest in their name like this one)
If their isn't a test annotation for your spring Authentication type in the repo yet, open an issue. I might consider implement it.

issue with Spring and asynchronous controller + HandlerInterceptor + IE/Edge

I am working on a Spring application that serves up REST endpoints. One of the endpoints essentially acts as a proxy between the HTML client and a third party cloud storage provider. This endpoint retrieves files from the storage provider and proxies them back to the client. Something like the following (note there is a synchronous and asynchronous version of the same endpoint):
#Controller
public class CloudStorageController {
...
#RequestMapping(value = "/fetch-image/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> fetchImageSynchronous(#PathVariable final Long id) {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
}
#RequestMapping(value = "/fetch-image-async/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public Callable<ResponseEntity<byte[]>> fetchImageAsynchronous(#PathVariable final Long id) {
return () -> {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
};
}
private byte[] fetchImage(final long id) {
// fetch the file from cloud storage and return as byte array
...
}
...
}
Due to the nature of the client app (HTML5 + ajax) and how this endpoint is used, user authentication is supplied to this endpoint differently that the other endpoints. To handle this, a HandlerInterceptor was developed to deal with authentication for this endpoint:
#Component("cloudStorageAuthenticationInterceptor")
public class CloudStorageAuthenticationInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
// examine the request for the authentication information and verify it
final Authentication authenticated = ...
if (authenticated == null) {
try {
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
throw new RuntimeException(e);
}
return false;
}
else {
try {
request.login(authenticated.getName(), (String) authenticated.getCredentials());
} catch (final ServletException e) {
throw new BadCredentialsException("Bad credentials");
}
}
return true;
}
}
The interceptor is registered like this:
#Configuration
#EnableWebMvc
public class ApiConfig extends WebMvcConfigurerAdapter {
#Autowired
#Qualifier("cloudStorageAuthenticationInterceptor")
private HandlerInterceptor cloudStorageAuthenticationInterceptor;
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(this.cloudStorageAuthenticationInterceptor)
.addPathPatterns(
"/fetch-image/**",
"/fetch-image-async/**"
);
}
#Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(this.asyncThreadPoolCoreSize);
executor.setMaxPoolSize(this.asyncThreadPoolMaxSize);
executor.setQueueCapacity(this.asyncThreadPoolQueueCapacity);
executor.setThreadNamePrefix(this.asyncThreadPoolPrefix);
executor.initialize();
configurer.setTaskExecutor(executor);
super.configureAsyncSupport(configurer);
}
}
Ideally, the image fetching would be done asynchronously (using the /fetch-image-asyc/{id} endpoint) because it has to call a third party web service which could have some latency.
The synchronous endpoint (/fetch-image/{id}) works correctly for all browsers. However, if using the asynchronous endpoint (/fetch-image-async/{id}), Chrome and Firefox work as expect.
However, if the client is Microsoft IE or Microsoft Edge, we seem some strange behavior. The endpoint is called correctly and the response sent successfully (at least from the server's viewpoint). However, it seems that the browser is waiting for something additional. In the IE/Edge DevTools window, the network request for the image shows as pending for 30 seconds, then seems to timeout, updates to successful and the image is successfully display. It also seems the connection to the server is still open, as the server side resources like database connections are not released. In the other browsers, the async response is received and processed in a second or less.
If I remove the HandlerInterceptor and just hard-wire some credentials for debugging, the behavior goes away. So this seems to have something to with the interaction between the HandlerInterceptor and the asynchronous controller method, and is only exhibited for some clients.
Anyone have a suggestion on why the semantics of IE/Edge are causing this behavior?
Based on your description, there are some different behaviors when using IE or Edge
it seems that the browser is waiting for something additional
the connection seems still open
it works fine if remove HandlerInterceptor and use hard code in auth logic
For the first behavior, I would suggest you use fiddler to trace all http requests. It is better if you could compare two different actions via fiddler (1) run on chrome, 2) run on edge ). Check all http headers in requests and responses carefully to see whether there is some different part. For the other behaviors, I would suggest you write logs to find which part spend the most time. It will provide you useful information to troubleshot.
After much tracing on the server and reading through the JavaDocs comments for AsyncHandlerInterceptor, I was able to resolve the issue. For requests to asynchronous controller methods, the preHandle method of any interceptor is called twice. It is called before the request is handed off to the servlet handling the request and again after the servlet has handled the request. In my case, the interceptor was attempting to authenticate the request for both scenarios (pre and post request handling). The application's authentication provider checks credentials in a database. For some reason if the client is IE or Edge, the authentication provider was unable to get a database connection when called from preHandle in the interceptor after the servlet handled the request. The following exception would be thrown:
ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: Could not open connection; nested exception is org.hibernate.exception.JDBCConnectionException: Could not open connection] with root cause
java.sql.SQLTransientConnectionException: HikariPool-0 - Connection is not available, request timed out after 30001ms.
So the servlet would successfully handle the request and send a response, but the filter would get hung up for 30 seconds waiting for the database connection to timeout on the post processing called to preHandle.
So for me, the simple solution was to add a check in preHandle if it is being called after the servlet has already handled the request. I updated the preHandle method as follows:
#Override
public boolean preHandle(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final Object pHandler) {
if (pRequest.getDispatcherType().equals(DispatcherType.REQUEST)) {
... perform authentication ...
}
return true;
}
That solved the issue for me. It doesn't explain everything (i.e., why only IE/Edge would cause the issue), but it seems that preHandle should only do work before the servlet handles the request anyways.

Spring Boot - Integration Testing with HTTPS

I have a Spring Boot app that normally runs on tomcat (as a normal WAR), and for some URL patterns I have Spring-Security setup to force it to be HTTPS. All the HTTPS/certificates stuff is done outside of my application, so all this step (at least appears) to be doing is redirecting to the HTTPS address (and port).
My security configuration:
http.requiresChannel().requestMatchers( "/**" ) .requiresSecure()
It all works as expected on tomcat, and I get redirected correctly.
Now I want to write some Spring #IntegrationTests, but am not able to get it to make requests to the HTTPS URL - I suspect I am missing something fairly obvious, but not having any joy with my attempts.
My basic test case:
#RunWith( SpringJUnit4ClassRunner )
#SpringApplicationConfiguration( classes = Application )
#WebAppConfiguration
#IntegrationTest( [ "server.port:0" ] )
class MultipleUserAuthenticationTest {
#Value('${local.server.port}') private int port
private URL base
private RestTemplate template
#Before public void setUp() throws Exception {
this.base = new URL("https://localhost:" + port + "/api")
template = new RestTemplate()
}
#Test public void apiRequest() throws Exception {
HttpHeaders headers = //set custom headers
HttpEntity<String> entity = new HttpEntity<String>( headers )
ResponseEntity<String> exchange = template.exchange( base.toString(), HttpMethod.GET, entity, String )
}
I have tried the approach outlined here https://stackoverflow.com/a/34411279/258813 and https://stackoverflow.com/a/24491820/258813
But no joy - It still seems to be failing because the port it is attempting to access is the HTTP port, so I basically get:
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:52002/api":Unrecognized SSL message, plaintext connection?; nested exception is javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
Am I missing something obvious? Is there another way to get the HTTPS port (or tell spring to use one)?

Spring Mvc REST api test | jQuery serialized form data-binding

First of all, i'm not a pro with tests, actually i'm learning to use them, and i'm finding myself in a lot of trouble trying to test Spring Controllers with the Spring Test framework, so i did the test after the production code, because i didn't know exactly how build the test.
The project is a Spring Boot (1.2.3) with Web, Thymeleaf, Security, MongoDB and Actuator modules, and using the platform-bom (1.1.2) for version management.
The functionality is easy, while the user fills a form, ajax requests using jQuery are sent to the server to verify the last field the user filled, then the server performs validation, and returns a JSON response.
Client side validation is not an option because some information has to be queried to a database to perform the validation (like if a username is already in use or not), so it needs to be done server side.
Also all the validation is performed with Hibernate Validator and custom annotations, both field and class annotations, since we use 1 entry point to perform the interactive form validation, the entire form is send in each request.
Here is the method signature (and first validations) in a #RestController :
#RequestMapping(value = "/some-url", method = RequestMethod.POST)
public ResponseEntity<ValidationResponse> validateFormField(
#Valid #ModelAttribute UserRegistrationForm form, BindingResult result) {
if (form == null || form.getSomeValue() == null) {
return new ResponseEntity<ValidationResponse>(HttpStatus.BAD_REQUEST);
}
// more input validation and actions
}
Now the call code, using jQuery:
$.ajax({
url: '/some-url',
type: 'post',
data: $(selector).serialize(),
dataType: 'json',
...
});
Now, the unit test code i'm writting to automatic test this is:
#ActiveProfiles("testing")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {
Application.class, WebConfiguration.class,
MongoConfiguration.class, SecurityConfiguration.class
})
#WebAppConfiguration
public class RegistrationInteractiveValidationRestControllerTest {
#Resource
private FilterChainProxy springSecurityFilterChain;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = webAppContextSetup(wac)
.addFilter(springSecurityFilterChain)
.build();
}
#Test
public void withValidInput_returnStatusOK() throws Exception {
// test init
MockHttpServletRequestBuilder ajaxRequest = post("/some-url")
.content( "username=Bobby&email=& ...more fields... " )
.secure( true ) .with( csrf().asHeader() );
// test action
ResultActions performRequest = mockMvc.perform(ajaxRequest);
// test verification
performRequest.andExpect( status().isOk() );
}
#Test
public void withInvalidInput_returnStatusBadRequest() throws Exception {
// test init
MockHttpServletRequestBuilder ajaxRequest = post("/some-url")
.content( "{ 'im' : 'a bad content request' }" )
.secure( true ) .with( csrf().asHeader() );
// test action
ResultActions performRequest = mockMvc.perform(ajaxRequest);
// test verification
performRequest.andExpect(status().isBadRequest());
}
}
The second test goes alright, status code is 400 as expected, but in the first test, it fails because it also returns a 400.
I did some test debugging and the form object itself is not null, but all its fields are null.
But if i deploy the app in a server, and do the test manually, it works just fine, ajax calls get responses with valid JSON and HTTP 200 status codes.
I also did some debugging of the app deployed in a server and the databinding works just fine.
My thoughts are that i'm missing something building the request in the test, so what i've tried is to verify in the browser the request headers and form data when the app is deployed on a server, and added those to the requests i'm building in the tests, all of them, without any luck at all.
I've researched also the Spring Framework, Spring Boot, Spring security and Spring Test reference documentation, and I didn't find anything that could lead me to the right answer.

Resources