I need to test the functionality of rest end point using Rest Template.
Below is the dummy rest api i need to test
#GetMapping(value = "/test")
public String getText(){
RestTemplate restTemplate = new RestTemplate();
String value = restTemplate.getForObject("EXTERNALURL/helloWorld",String.class);
return value +" got the value";
}
I have the below test cases which hits the above rest end point
private void expectFixedData() {
MockServerClient mockServerClient = ClientAndServer.startClientAndServer("127.0.0.1",1080);
try {
mockServerClient.when(HttpRequest.request().withMethod("GET")
.withPath("/helloWorld"), Times.exactly(1))
.respond(HttpResponse.response().withStatusCode(200)
.withBody("{ 'incorrect username and password combination' }")
.withDelay(TimeUnit.SECONDS, 1));
}
finally{
mockServerClient.close();
}
}
#Test
public void callExternalService(){
expectFixedData();
RestTemplate restTemplate = new RestTemplate();
String value = restTemplate.getForObject("http://localhost:8080/test",String.class);
Assert.assertTrue(value.equals("incorrect username and password combination got the value"));
}
But when i run the test cases it still calls the external URL
Any help would be appreciated.
Was able to mock response for 3rd party using WireMock
Related
I'm trying to run some integration tests on my code and I use a MockRestServiceServer from spring-boot-test in order to set up the expected requests.
I have one call that is called many times while running my test, but it seems not to persist during the test. My test looks like this:
#Test
void getHealthStatus() {
try {
RequestBuilder request = get("/actuator/hc").contentType("application/json");
MockServerBinder.bindPersistentThingworxPropertiesCall(
mockServer,
requestTo(new URI(String.format("%sProperties/TestProp", thingworxUrl))),
objectMapper.writeValueAsString(new PingResponse(DashboardIndicator.HEALTHY, 200))
);
DashboardStatusModel expectedResult = new DashboardStatusModel();
expectedResult.addResult("spring",service.getAppHealth());
expectedResult.addResult("thingworx", service.getThingworxAvailability());
assertOpenUrl(request);
MvcResult result = mockMvc.perform(get("/actuator/hc").contentType("application/json"))
.andExpect(status().isOk())
.andReturn();
DashboardStatusModel actualResult = objectMapper.readValue(result.getResponse().getContentAsString(), DashboardStatusModel.class);
assertEquals(expectedResult.getResults().get("spring"), actualResult.getResults().get("spring"));
assertEquals(expectedResult.getResults().get("thingworx").getStatus(),actualResult.getResults().get("thingworx").getStatus());
assertEquals(expectedResult.getResults().get("thingworx").getData().get("url"), actualResult.getResults().get("thingworx").getData().get("url"));
} catch (Exception e) {
fail("Unable to perform REST call on GDP-API", e);
}
}
As additional information:
mockServer is created in a superclass like this:
protected static MockRestServiceServer mockServer;
#BeforeEach
public void configureMockServer() {
mockServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
}
MockServerBinder.bindPersistentThingworxPropertiesCall() is a helper class that looks like this:
public static void bindPersistentThingworxPropertiesCall(MockRestServiceServer mockServer, RequestMatcher request, String responseJSONasString){
mockServer.expect(ExpectedCount.once(), request)
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(responseJSONasString));
}
assertOpenUrl(request); is a function that checks if a URL doesn't have any authentication by using a MockMVC:
public void assertOpenUrl(RequestBuilder request){
try{
mockMvc.perform(request).andExpect(status().isOk());
} catch (Exception e) {
fail("Unable to perform REST call on GDP-API", e);
}
}
When I run this test, the expectedResult.addResult("thingworx", service.getThingworxAvailability()); will be able to use the MockRestServiceServer, but the assertOpenUrl(request); line will fail, because MockRestServiceServer doesn't expect anymore calls to the endpoint binded in MockServerBinder.bindPersistantThingworxPropertyCall(). This does not happen if I Copy & Paste MockServerBinder.bindPersistantThingworxPropertyCall() under the existing one, so I think it's a problem with how I binded the request in the first place.
From what I understand ExpectedCount.manyTimes() should keep this request during the test.
Is this not true or is there another way I should bind my request so that it stays available during the entire test?
PEBCAK issue.
As you can see in the bindPersistentThingworxPropertiesCall(), I actually didn't use ExpectedCount.manyTimes(). I didn't catch that. Changed it and now it works.
I am using a method where it calls another REST API to retrieve an ID from the DB. When I run the veracode scan for the class I am getting Security flaw "Server-side Request Forgery" at below line.
response = resttemplate.getForEntity(resturl, String.class);
Not sure How to fix this issue. Any help is appreciated. Below is my full code for that method.
public static String getIDFromDB(String resturl) {
String id = null;
RestTemplate resttemplate = new RestTemplate();
ResponseEntity<String> response = new ResponseEntity<>(HTTPStatus.OK)
try {
response = resttemplate.getForEntity(resturl, String.class);
if (response.getStatusCode == HTTPStatus.OK && response.getBody.trim() != null) {
id = response.getBody.trim() ;
}
} Catch(Exception e) {
log.error("failed to get msgID: {}", e);
}
}
This is because you are allowing in your code to pass the resturl completely in your code, so it enables the attacker to bypass and route the URL to their intended destination.
To avoid this, so should externalise and refer the URL having domain and the application contexts with operation name in config files or dB
From my backend application(springboot, java8) i will make multiple external api call's. I have a requirement to log all the requests and response data's (including headers, request and response body) into database(MongoDB).
Below is my sample code, this how i am trying to capture request and responses on each external api calls. On exception i will store status as 'FAILED'.
In my project multiple modules will be added on new 3rd party api integration, so in each module for every different external api calls i have to capture all the requests and reponses like this. I am not satisfied with below approach. Kindly suggest best approach to solve this.
Sample Service layer method
public ResponseDTOFromExternalApi externalApiCallServiceMethod(String clientId, RequestDTO requestDTO) {
ExternalApiCallRequestObj externalApiCallRequestObj = prepareExternalApiRequestObjFromRequestDTO(requestDTO);
ApiCall apiCall = ApiCall.builder()
.clientId(clientId)
.status("SUBMITTED")
.requestPayload(externalApiCallRequestObj)
.build();
apiCall = apiCallRepository.save(apiCall);
ExternalApiCallReponseObj externalApiCallReponseObj = externalApiCallService.callExternalApi1(externalApiCallRequestObj);
apiCall = apiCallRepository.findById(apiCall.getId());
apiCall.setResponsePayload(externalApiCallReponseObj);
apiCall.setStatus("COMPLETED");
apiCallRepository.save(apiCall);
return toDTO(externalApiCallReponseObj);
}
Sample Domain for api calls
#Document("api_calls")
#Builder
#Data
public class ApiCall {
#Id
private String id;
private String clientId;
private String status;
private Object requestPayload;
private Object responsePayload;
}
Spring's WebClient already has the ability to log all request and response data by adding exchange filters.
By using it for your network requests the only thing left to do is to write this information in your mongodb.
Here is tutorial on logging requests and responses:
https://www.baeldung.com/spring-log-webclient-calls
Cheers
You may use Spring AOP to address this cross cutting concern.
Assuming ExternalApiCallService is a spring managed bean , following code will intercept all the callExternalApi1() and can log the same to database.
#Component
#Aspect
public class ExternalCallLoggerAspect {
#Autowired
ApiCallRepository apiCallRepository;
#Pointcut("execution(* *..ExternalApiCallService.callExternalApi1(..))")
public void externalApiCallService(){}
#Around("externalApiCallService() && args(request)")
public ExternalApiCallReponseObj logCalls(ProceedingJoinPoint pjp,ExternalApiCallRequestObj request){
Object result=null;
String status = "COMPLETED";
ExternalApiCallReponseObj response = null;
// Build the apiCall from request
ApiCall apiCall = ApiCall.builder()
.clientId(clientId)
.status("SUBMITTED")
.requestPayload(request)
.build();
//save the same to db
apiCall = apiCallRepository.save(apiCall);
// Proceed to call the external Api and get the result
try {
result = pjp.proceed();
} catch (Throwable e) {
status = "FAILED";
}
//Update the response
apiCall = apiCallRepository.findById(apiCall.getId());
apiCall.setStatus(status);
apiCallRepository.save(apiCall);
if(result != null) {
response = (ExternalApiCallReponseObj)result;
apiCall.setResponsePayload(response);
}
//continue with response
return response;
}
}
Note
1.There is a typo with the name ExternalApiCallReponseObj
2.The aspect code is verified that it works and the logic was included later on untested. Please make the required corrections
Ideally the original method should be stripped down to this
public ResponseDTOFromExternalApi externalApiCallServiceMethod(String clientId, RequestDTO requestDTO) {
return toDTO(externalApiCallService.callExternalApi1(prepareExternalApiRequestObjFromRequestDTO(requestDTO)));
}
More about Spring AOP here
Update : on a second thought , if all the external api calls are through a single method , say ExternalApiCallService.callExternalApi1() , this logging logic can be moved to that common point , isn't it ?
I use external API, which returns list of sorted by date Objects with many (approx. 30) properties.
I wrote simple Rest API using Spring Boot with one endpoint
/newest_obj_name
which just return currently newest name of Object from that list and ignore everything else.
How can I sufficiently test that code while the value from external API is constantly changing, so I cannot simply use String expected as in a code below?
Generally speaking how to approach whole testing issue in that scenario?
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyTest {
#LocalServerPort
private int port;
private TestRestTemplate restTemplate = new TestRestTemplate();
private HttpHeaders headers = new HttpHeaders();
#Test
public void testRetrieveNewest() {
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/newest_obj_name"),
HttpMethod.GET, entity, String.class);
String expected = "{\"name\":\"crazy\"}";
try {
JSONAssert.assertEquals(expected, response.getBody(), false);
} catch (JSONException e) {
e.printStackTrace();
}
}
private String createURLWithPort(String uri) {
return "http://localhost:" + port + uri;
}
}
I'm getting error code "405" while testing my API calls on run mode in IntelliJ. However when I test the API cal; with the same parameters in Postman it works.
I'm programming a jukebox where an user has to log in in order to use it. The login form works and when i try to log in with Postman it also logs in succesfull. However when i run my code on IntelliJ it gives me error code "405", which means "Method not allowed".
My userController class code which works:
#PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String login(#RequestBody MultiValueMap<String, String> body){
String email = body.getFirst("email");
String username = body.getFirst("username");
String password = body.getFirst("password");
return userService.loginUser(email, username, password);
}
My functional testing code(also works because my other GET methods in my API tests works):
public class FunctionalTest {
#BeforeClass
public static void setup() {
String port = System.getProperty("server.port");
if (port == null) {
RestAssured.port = Integer.valueOf(8080);
}
else{
RestAssured.port = Integer.valueOf(port);
}
String basePath = System.getProperty("server.base");
if(basePath==null){
basePath = "/";
}
RestAssured.basePath = basePath;
String baseHost = System.getProperty("server.host");
if(baseHost==null){
baseHost = "http://localhost";
}
RestAssured.baseURI = baseHost;
}
}
And finally my code for testing the POST method for logging in:
//User control
#Test
public void checkLogin(){
given()
.param("email", "123")
.param("username", "123")
.param("password", "123")
.when()
.get("/login")
.then()
.statusCode(200);
}
Hope anyone can help me solve this problem.
Thanks in advance!
405 means method not allowed. This is happening because you are exposing a POST operation (#PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) but trying to consume by means of a GET: .when().get("/login")