Skip to content

Commit

Permalink
More coverage to provide more test cases & also remove unused codes.
Browse files Browse the repository at this point in the history
More coverage to provide more test cases & also remove unused codes.
  • Loading branch information
infstar committed Sep 12, 2024
1 parent 29e87d4 commit a1a5d28
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,40 +110,6 @@ public <T> T get(String url, Class<T> clazz) {
return obj;
}

public <T> T get(String url, MultiValueMap<String, String> queryParams, Class<T> clazz) {
T obj;
try {
obj = this.batchWebClient
.get()
.uri(uriBuilder -> uriBuilder
.path(url).queryParams(queryParams)
.build()
)
.headers(h -> h.set(EducGradBatchGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()))
.retrieve()
// if 5xx errors, throw Service error
.onStatus(HttpStatusCode::is5xxServerError,
clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, ERROR_MESSAGE1), clientResponse.statusCode().value())))
.bodyToMono(clazz)
// only does retry if initial error was 5xx as service may be temporarily down
// 4xx errors will always happen if 404, 401, 403 etc., so does not retry
.retryWhen(reactor.util.retry.Retry.backoff(constants.getDefaultRetryMaxAttempts(), Duration.ofSeconds(constants.getDefaultRetryWaitDurationSeconds()))
.filter(ServiceException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
throw new ServiceException(getErrorMessage(url, ERROR_MESSAGE2), HttpStatus.SERVICE_UNAVAILABLE.value());
}))
.block();
} catch (Exception e) {
// catches IOExceptions and the like
throw new ServiceException(getErrorMessage(
url,
e.getLocalizedMessage()),
(e instanceof WebClientResponseException) ? ((WebClientResponseException) e).getStatusCode().value() : HttpStatus.SERVICE_UNAVAILABLE.value(),
e);
}
return obj;
}

/**
* Generic POST call out to services. Uses blocking webclient and will throw
* runtime exceptions. Will attempt retries if 5xx errors are encountered.
Expand All @@ -158,7 +124,7 @@ public <T> T get(String url, MultiValueMap<String, String> queryParams, Class<T>
public <T> T post(String url, Object body, Class<T> clazz, String accessToken) {
T obj;
try {
obj = webClient.post()
obj = this.webClient.post()
.uri(url)
.headers(h -> { h.setBearerAuth(accessToken); h.set(EducGradBatchGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); })
.body(BodyInserters.fromValue(body))
Expand Down Expand Up @@ -186,7 +152,7 @@ public <T> T post(String url, Object body, Class<T> clazz, String accessToken) {
public <T> T post(String url, Object body, Class<T> clazz) {
T obj;
try {
obj = batchWebClient.post()
obj = this.batchWebClient.post()
.uri(url)
.headers(h -> h.set(EducGradBatchGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()))
.body(BodyInserters.fromValue(body))
Expand Down Expand Up @@ -218,42 +184,13 @@ public <T> T post(String url, Object body, Class<T> clazz) {
* @param url the url you are calling
* @param body the body you are requesting
* @param clazz the return type you are expecting
* @param accessToken access token
* @return return type
* @param <T> expected return type
*/
public <T> T put(String url, Object body, Class<T> clazz, String accessToken) {
T obj;
try {
obj = this.webClient.put()
.uri(url)
.headers(h -> { h.setBearerAuth(accessToken); h.set(EducGradBatchGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); })
.body(BodyInserters.fromValue(body))
.retrieve()
.onStatus(HttpStatusCode::is5xxServerError,
clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, ERROR_MESSAGE1), clientResponse.statusCode().value())))
.bodyToMono(clazz)
.retryWhen(reactor.util.retry.Retry.backoff(constants.getDefaultRetryMaxAttempts(), Duration.ofSeconds(constants.getDefaultRetryWaitDurationSeconds()))
.filter(ServiceException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
throw new ServiceException(getErrorMessage(url, ERROR_MESSAGE2), HttpStatus.SERVICE_UNAVAILABLE.value());
}))
.block();
} catch (Exception e) {
// catches IOExceptions and the like
throw new ServiceException(getErrorMessage(
url,
e.getLocalizedMessage()),
(e instanceof WebClientResponseException) ? ((WebClientResponseException) e).getStatusCode().value() : HttpStatus.SERVICE_UNAVAILABLE.value(),
e);
}
return obj;
}

public <T> T put(String url, Object body, Class<T> clazz) {
T obj;
try {
obj = this.webClient.put()
obj = this.batchWebClient.put()
.uri(url)
.headers(h -> h.set(EducGradBatchGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()))
.body(BodyInserters.fromValue(body))
Expand All @@ -278,11 +215,19 @@ public <T> T put(String url, Object body, Class<T> clazz) {
return obj;
}

public <T> T delete(String url, Class<T> boundClass, String accessToken) {
/**
* Generic DELETE call out to services. Uses blocking webclient and will throw
* runtime exceptions. Will attempt retries if 5xx errors are encountered.
* You can catch Exception in calling method.
* @param url the url you are calling
* @return return type
* @param <T> expected return type
*/
public <T> T delete(String url, Class<T> boundClass) {
T obj;
try {
obj = webClient.delete().uri(url)
.headers(h -> { h.setBearerAuth(accessToken); h.set(EducGradBatchGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); })
obj = this.batchWebClient.delete().uri(url)
.headers(h -> h.set(EducGradBatchGraduationApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()))
.retrieve().bodyToMono(boundClass).block();
} catch(Exception e) {
// catches IOExceptions and the like
Expand All @@ -295,12 +240,6 @@ public <T> T delete(String url, Class<T> boundClass, String accessToken) {
return obj;
}

protected String parseUrlParameters(String url, String... params) {
List<String> l = Arrays.asList(params);
l.replaceAll(t-> Objects.isNull(t) ? "" : t);
return String.format(url, l.toArray());
}

private String getErrorMessage(String url, String errorMessage) {
return "Service failed to process at url: " + url + " due to: " + errorMessage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,7 @@ public void updateSchoolReportRecord(String schoolOfRecord, String reportTypeCod

public void deleteSchoolReportRecord(String schoolOfRecord, String reportTypeCode) {
ThreadLocalStateUtil.setCorrelationID(UUID.randomUUID().toString());
String accessToken = getAccessToken();
restService.delete(String.format(constants.getUpdateSchoolReport(),schoolOfRecord,reportTypeCode), Boolean.class, accessToken);
restService.delete(String.format(constants.getUpdateSchoolReport(),schoolOfRecord,reportTypeCode), Boolean.class);
}

public List<StudentCredentialDistribution> getStudentsForUserReqDisRun(String credentialType, StudentSearchRequest req) {
Expand Down Expand Up @@ -469,21 +468,17 @@ public void updateStudentGradRecord(UUID studentID, Long batchId,String activity
}
}

public void updateStudentGradRecordHistory(List<UUID> studentIDs, Long batchId, String accessToken, String userName, String activityCode) {
public void updateStudentGradRecordHistory(List<UUID> studentIDs, Long batchId, String userName, String activityCode) {
try {
if (batchId != null) {
String url = String.format(constants.getUpdateStudentRecordHistory(), batchId, userName, activityCode);
restService.put(url,studentIDs, GraduationStudentRecord.class, accessToken);
restService.put(url,studentIDs, GraduationStudentRecord.class);
}
} catch (Exception e) {
LOGGER.error("Unable to update student record history {}", e.getLocalizedMessage());
}
}

public void updateStudentGradRecordHistory(List<UUID> studentIDs, Long batchId, String userName, String activityCode) {
updateStudentGradRecordHistory(studentIDs, batchId, getAccessToken(), userName, activityCode);
}

public String updateStudentFlagReadyForBatch(List<UUID> studentIds, String batchJobType) {
ThreadLocalStateUtil.setCorrelationID(UUID.randomUUID().toString());
StudentList stuList = new StudentList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package ca.bc.gov.educ.api.batchgraduation.util;

import ca.bc.gov.educ.api.batchgraduation.exception.ServiceException;
import ca.bc.gov.educ.api.batchgraduation.rest.RESTService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.function.Consumer;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@SpringBootTest
@RunWith(SpringRunner.class)
@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
public class RESTServiceDeleteTest {

@Autowired
@InjectMocks
private RESTService restService;

@Mock
private WebClient.RequestHeadersSpec requestHeadersMock;
@Mock
private WebClient.RequestHeadersUriSpec requestHeadersUriMock;
@Mock
private WebClient.RequestBodySpec requestBodyMock;
@Mock
private WebClient.RequestBodyUriSpec requestBodyUriMock;
@Mock
private WebClient.ResponseSpec responseMock;

@MockBean
@Qualifier("webClient")
WebClient webClient;

@MockBean
@Qualifier("batchClient")
WebClient batchWebClient;

@Mock
private ClientRegistrationRepository clientRegistrationRepositoryMock;

@Mock
private OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepositoryMock;

private static final String TEST_URL_200 = "https://httpstat.us/200";
private static final String TEST_URL_403 = "https://httpstat.us/403";
private static final String TEST_URL_503 = "https://httpstat.us/503";
private static final String OK_RESPONSE = "200 OK";

@Before
public void setUp(){
when(this.batchWebClient.delete()).thenReturn(this.requestHeadersUriMock);
when(this.requestHeadersUriMock.uri(any(String.class))).thenReturn(this.requestHeadersMock);
when(this.requestHeadersMock.headers(any(Consumer.class))).thenReturn(this.requestHeadersMock);
when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock);
when(this.responseMock.onStatus(any(), any())).thenReturn(this.responseMock);
}

@Test
public void testDelete_GivenProperData_Expect200Response(){
when(this.responseMock.bodyToMono(String.class)).thenReturn(Mono.just(OK_RESPONSE));
String response = this.restService.delete(TEST_URL_200, String.class);
Assert.assertEquals("200 OK", response);
}

@Test(expected = ServiceException.class)
public void testDelete_Given5xxErrorFromService_ExpectServiceError(){
when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException()));
this.restService.delete(TEST_URL_503, String.class);
}

@Test(expected = ServiceException.class)
public void testDelete_Given4xxErrorFromService_ExpectServiceError(){
when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException()));
this.restService.delete(TEST_URL_403, String.class);
}

}
Original file line number Diff line number Diff line change
@@ -1,42 +1,115 @@
package ca.bc.gov.educ.api.batchgraduation.util;

import ca.bc.gov.educ.api.batchgraduation.exception.ServiceException;
import ca.bc.gov.educ.api.batchgraduation.rest.RESTService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.function.Consumer;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
public class RESTServiceGetTest {

@Autowired
private RESTService restService;

//@Test
@Mock
private WebClient.RequestHeadersSpec requestHeadersMock;
@Mock
private WebClient.RequestHeadersUriSpec requestHeadersUriMock;
@Mock
private WebClient.RequestBodySpec requestBodyMock;
@Mock
private WebClient.RequestBodyUriSpec requestBodyUriMock;
@Mock
private WebClient.ResponseSpec responseMock;

@MockBean
@Qualifier("webClient")
WebClient webClient;

@MockBean
@Qualifier("batchClient")
WebClient batchWebClient;

@Mock
private ClientRegistrationRepository clientRegistrationRepositoryMock;

@Mock
private OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepositoryMock;

private static final String TEST_URL_200 = "https://httpstat.us/200";
private static final String TEST_URL_403 = "https://httpstat.us/403";
private static final String TEST_URL_503 = "https://httpstat.us/503";
private static final String OK_RESPONSE = "200 OK";

@Before
public void setUp(){
when(this.webClient.get()).thenReturn(this.requestHeadersUriMock);
when(this.batchWebClient.get()).thenReturn(this.requestHeadersUriMock);
when(this.requestHeadersUriMock.uri(any(String.class))).thenReturn(this.requestHeadersMock);
when(this.requestHeadersMock.headers(any(Consumer.class))).thenReturn(this.requestHeadersMock);
when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock);
when(this.responseMock.onStatus(any(), any())).thenReturn(this.responseMock);
}

@Test
public void testGet_GivenProperData_Expect200Response(){
String response;
response = this.restService.get("https://httpstat.us/200", String.class, "1234");
when(this.responseMock.bodyToMono(String.class)).thenReturn(Mono.just(OK_RESPONSE));
String response = this.restService.get(TEST_URL_200, String.class, "1234");
Assert.assertEquals("200 OK", response);
}

//@Test(expected = ServiceException.class)
@Test
public void testGetOverride_GivenProperData_Expect200Response(){
when(this.responseMock.bodyToMono(String.class)).thenReturn(Mono.just(OK_RESPONSE));
String response = this.restService.get(TEST_URL_200, String.class);
Assert.assertEquals(OK_RESPONSE, response);
}

@Test(expected = ServiceException.class)
public void testGet_Given5xxErrorFromService_ExpectServiceError(){
this.restService.get("https://httpstat.us/503", String.class, "1234");
when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException()));
this.restService.get(TEST_URL_503, String.class, "1234");
}

@Test(expected = ServiceException.class)
public void testGetOverride_Given5xxErrorFromService_ExpectServiceError(){
when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException()));
this.restService.get(TEST_URL_503, String.class);
}

//@Test(expected = ServiceException.class)
@Test(expected = ServiceException.class)
public void testGet_Given4xxErrorFromService_ExpectServiceError(){
this.restService.get("https://httpstat.us/403", String.class, "1234");
when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException()));
this.restService.get(TEST_URL_403, String.class, "1234");
}

@Test
public void testDoNothing() {
Assert.assertTrue(true);
@Test(expected = ServiceException.class)
public void testGetOverride_Given4xxErrorFromService_ExpectServiceError(){
when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException()));
this.restService.get(TEST_URL_403, String.class);
}

}
Loading

0 comments on commit a1a5d28

Please sign in to comment.