Add test coverage (some failing) for DSTU2 ClientUtils

This commit is contained in:
dotasek 2024-11-01 09:12:37 -04:00
parent c5aaf43121
commit a76078a5a5
5 changed files with 391 additions and 342 deletions

View File

@ -28,6 +28,12 @@
<artifactId>org.hl7.fhir.utilities</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- UCUM -->
<dependency>
<groupId>org.fhir</groupId>
@ -81,6 +87,24 @@
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>

View File

@ -48,6 +48,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -98,47 +100,37 @@ public class ClientUtils {
public static final String HEADER_LOCATION = "location";
private static boolean debugging = false;
@Getter
@Setter
private HttpHost proxy;
@Getter
@Setter
private int timeout = 5000;
@Getter
@Setter
private String username;
@Getter
@Setter
private String password;
@Setter
@Getter
private ToolingClientLogger logger;
@Setter
@Getter
private int retryCount;
@Getter
@Setter
private String userAgent;
private String acceptLang;
private String contentLang;
public HttpHost getProxy() {
return proxy;
}
public void setProxy(HttpHost proxy) {
this.proxy = proxy;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Setter
private String acceptLanguage;
@Setter
private String contentLanguage;
public <T extends Resource> ResourceRequest<T> issueOptionsRequest(URI optionsUri, String resourceFormat,
int timeoutLoading) {
@ -247,7 +239,6 @@ public class ClientUtils {
/**
* @param resourceFormat
* @param options
* @return
*/
protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request,
@ -257,7 +248,6 @@ public class ClientUtils {
/**
* @param resourceFormat
* @param options
* @return
*/
protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request,
@ -296,11 +286,11 @@ public class ClientUtils {
if (!Utilities.noString(userAgent)) {
request.addHeader("User-Agent", userAgent);
}
if (!Utilities.noString(acceptLang)) {
request.addHeader("Accept-Language", acceptLang);
if (!Utilities.noString(acceptLanguage)) {
request.addHeader("Accept-Language", acceptLanguage);
}
if (!Utilities.noString(contentLang)) {
request.addHeader("Content-Language", acceptLang);
if (!Utilities.noString(contentLanguage)) {
request.addHeader("Content-Language", acceptLanguage);
}
if (format != null) {
@ -363,9 +353,8 @@ public class ClientUtils {
/**
*
* @param request
* @param payload
* @return
* @param request The request to be sent
* @return The response from the server
*/
protected HttpResponse sendRequest(HttpUriRequest request) {
if (FhirSettings.isProhibitNetworkAccess()) {
@ -455,7 +444,7 @@ public class ClientUtils {
return feed;
}
private boolean hasError(OperationOutcome oo) {
protected boolean hasError(OperationOutcome oo) {
for (OperationOutcomeIssueComponent t : oo.getIssue())
if (t.getSeverity() == IssueSeverity.ERROR || t.getSeverity() == IssueSeverity.FATAL)
return true;
@ -688,14 +677,6 @@ public class ClientUtils {
return cnt;
}
public ToolingClientLogger getLogger() {
return logger;
}
public void setLogger(ToolingClientLogger logger) {
this.logger = logger;
}
/**
* Used for debugging
*
@ -714,26 +695,5 @@ public class ClientUtils {
return value;
}
public int getRetryCount() {
return retryCount;
}
public void setRetryCount(int retryCount) {
this.retryCount = retryCount;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public void setAcceptLanguage(String language) {
this.acceptLang = language;
}
public void setContentLanguage(String language) {
this.contentLang = language;
}
}

View File

@ -355,170 +355,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
return result.getPayload();
}
//
// public <T extends Resource> boolean delete(Class<T> resourceClass, String id) {
// try {
// return utils.issueDeleteRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), proxy);
// } catch(Exception e) {
// throw new EFhirClientException("An error has occurred while trying to delete this resource", e);
// }
//
// }
//
// public <T extends Resource> OperationOutcome create(Class<T> resourceClass, T resource) {
// ResourceRequest<T> resourceRequest = null;
// try {
// List<Header> headers = null;
// resourceRequest = utils.issuePostRequest(resourceAddress.resolveGetUriFromResourceClass(resourceClass),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), withVer(getPreferredResourceFormat(), "1.0"), headers, proxy);
// resourceRequest.addSuccessStatus(201);
// if(resourceRequest.isUnsuccessfulRequest()) {
// throw new EFhirClientException("Server responded with HTTP error code " + resourceRequest.getHttpStatus(), (OperationOutcome)resourceRequest.getPayload());
// }
// } catch(Exception e) {
// handleException("An error has occurred while trying to create this resource", e);
// }
// OperationOutcome operationOutcome = null;;
// try {
// operationOutcome = (OperationOutcome)resourceRequest.getPayload();
// ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier =
// ResourceAddress.parseCreateLocation(resourceRequest.getLocation());
// OperationOutcomeIssueComponent issue = operationOutcome.addIssue();
// issue.setSeverity(IssueSeverity.INFORMATION);
// issue.setUserData(ResourceAddress.ResourceVersionedIdentifier.class.toString(),
// resVersionedIdentifier);
// return operationOutcome;
// } catch(ClassCastException e) {
// // some server (e.g. grahams) returns the resource directly
// operationOutcome = new OperationOutcome();
// OperationOutcomeIssueComponent issue = operationOutcome.addIssue();
// issue.setSeverity(IssueSeverity.INFORMATION);
// issue.setUserData(ResourceRequest.class.toString(),
// resourceRequest.getPayload());
// return operationOutcome;
// }
// }
//
// public <T extends Resource> Bundle history(Calendar lastUpdate, Class<T> resourceClass, String id) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, lastUpdate, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history information for this resource", e);
// }
// return history;
// }
//
// public <T extends Resource> Bundle history(Date lastUpdate, Class<T> resourceClass, String id) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, lastUpdate, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history information for this resource", e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle history(Calendar lastUpdate, Class<T> resourceClass) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, lastUpdate, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history information for this resource type", e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle history(Date lastUpdate, Class<T> resourceClass) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, lastUpdate, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history information for this resource type", e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle history(Class<T> resourceClass) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history information for this resource type", e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle history(Class<T> resourceClass, String id) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history information for this resource", e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle history(Date lastUpdate) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(lastUpdate, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history since last update",e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle history(Calendar lastUpdate) {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(lastUpdate, maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history since last update",e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle history() {
// Bundle history = null;
// try {
// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(maxResultSetSize), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("An error has occurred while trying to retrieve history since last update",e);
// }
// return history;
// }
//
//
// public <T extends Resource> Bundle search(Class<T> resourceClass, Map<String, String> parameters) {
// Bundle searchResults = null;
// try {
// searchResults = utils.issueGetFeedRequest(resourceAddress.resolveSearchUri(resourceClass, parameters), withVer(getPreferredResourceFormat(), "1.0"), proxy);
// } catch (Exception e) {
// handleException("Error performing search with parameters " + parameters, e);
// }
// return searchResults;
// }
//
//
// public <T extends Resource> Bundle searchPost(Class<T> resourceClass, T resource, Map<String, String> parameters) {
// Bundle searchResults = null;
// try {
// searchResults = utils.issuePostFeedRequest(resourceAddress.resolveSearchUri(resourceClass, new HashMap<String, String>()), parameters, "src", resource, getPreferredResourceFormat());
// } catch (Exception e) {
// handleException("Error performing search with parameters " + parameters, e);
// }
// return searchResults;
// }
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) {
recordUse();
@ -588,101 +425,6 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
return (OperationOutcome) result.getPayload();
}
/*
* change to meta operations
*
* public List<Coding> getAllTags() { TagListRequest result = null; try { result
* = utils.issueGetRequestForTagList(resourceAddress.resolveGetAllTags(),
* withVer(getPreferredResourceFormat(), "1.0"), null, proxy); } catch (Exception e) {
* handleException("An error has occurred while trying to retrieve all tags",
* e); } return result.getPayload(); }
*
*
* public <T extends Resource> List<Coding> getAllTagsForResourceType(Class<T>
* resourceClass) { TagListRequest result = null; try { result =
* utils.issueGetRequestForTagList(resourceAddress.
* resolveGetAllTagsForResourceType(resourceClass),
* withVer(getPreferredResourceFormat(), "1.0"), null, proxy); } catch (Exception e) {
* handleException("An error has occurred while trying to retrieve tags for this resource type"
* , e); } return result.getPayload(); }
*
*
* public <T extends Resource> List<Coding> getTagsForReference(Class<T>
* resource, String id) { TagListRequest result = null; try { result =
* utils.issueGetRequestForTagList(resourceAddress.resolveGetTagsForReference(
* resource, id), withVer(getPreferredResourceFormat(), "1.0"), null, proxy); } catch (Exception
* e) {
* handleException("An error has occurred while trying to retrieve tags for this resource"
* , e); } return result.getPayload(); }
*
*
* public <T extends Resource> List<Coding> getTagsForResourceVersion(Class<T>
* resource, String id, String versionId) { TagListRequest result = null; try {
* result = utils.issueGetRequestForTagList(resourceAddress.
* resolveGetTagsForResourceVersion(resource, id, versionId),
* withVer(getPreferredResourceFormat(), "1.0"), null, proxy); } catch (Exception e) {
* handleException("An error has occurred while trying to retrieve tags for this resource version"
* , e); } return result.getPayload(); }
*
* // // public <T extends Resource> boolean deleteTagsForReference(Class<T>
* resourceClass, String id) { // try { // return
* utils.issueDeleteRequest(resourceAddress.resolveGetTagsForReference(
* resourceClass, id), proxy); // } catch(Exception e) { //
* handleException("An error has occurred while trying to retrieve tags for this resource version"
* , e); // throw new
* EFhirClientException("An error has occurred while trying to delete this resource"
* , e); // } // // } // // // public <T extends Resource> boolean
* deleteTagsForResourceVersion(Class<T> resourceClass, String id, List<Coding>
* tags, String version) { // try { // return
* utils.issueDeleteRequest(resourceAddress.resolveGetTagsForResourceVersion(
* resourceClass, id, version), proxy); // } catch(Exception e) { //
* handleException("An error has occurred while trying to retrieve tags for this resource version"
* , e); // throw new
* EFhirClientException("An error has occurred while trying to delete this resource"
* , e); // } // }
*
*
* public <T extends Resource> List<Coding> createTags(List<Coding> tags,
* Class<T> resourceClass, String id) { TagListRequest request = null; try {
* request =
* utils.issuePostRequestForTagList(resourceAddress.resolveGetTagsForReference(
* resourceClass, id),utils.getTagListAsByteArray(tags, false,
* isJson(getPreferredResourceFormat())), withVer(getPreferredResourceFormat(), "1.0"), null,
* proxy); request.addSuccessStatus(201); request.addSuccessStatus(200);
* if(request.isUnsuccessfulRequest()) { throw new
* EFhirClientException("Server responded with HTTP error code " +
* request.getHttpStatus()); } } catch(Exception e) {
* handleException("An error has occurred while trying to set tags for this resource"
* , e); } return request.getPayload(); }
*
*
* public <T extends Resource> List<Coding> createTags(List<Coding> tags,
* Class<T> resourceClass, String id, String version) { TagListRequest request =
* null; try { request = utils.issuePostRequestForTagList(resourceAddress.
* resolveGetTagsForResourceVersion(resourceClass, id,
* version),utils.getTagListAsByteArray(tags, false,
* isJson(getPreferredResourceFormat())), withVer(getPreferredResourceFormat(), "1.0"), null,
* proxy); request.addSuccessStatus(201); request.addSuccessStatus(200);
* if(request.isUnsuccessfulRequest()) { throw new
* EFhirClientException("Server responded with HTTP error code " +
* request.getHttpStatus()); } } catch(Exception e) {
* handleException("An error has occurred while trying to set the tags for this resource version"
* , e); } return request.getPayload(); }
*
*
* public <T extends Resource> List<Coding> deleteTags(List<Coding> tags,
* Class<T> resourceClass, String id, String version) { TagListRequest request =
* null; try { request = utils.issuePostRequestForTagList(resourceAddress.
* resolveDeleteTagsForResourceVersion(resourceClass, id,
* version),utils.getTagListAsByteArray(tags, false,
* isJson(getPreferredResourceFormat())), withVer(getPreferredResourceFormat(), "1.0"), null,
* proxy); request.addSuccessStatus(201); request.addSuccessStatus(200);
* if(request.isUnsuccessfulRequest()) { throw new
* EFhirClientException("Server responded with HTTP error code " +
* request.getHttpStatus()); } } catch(Exception e) {
* handleException("An error has occurred while trying to delete the tags for this resource version"
* , e); } return request.getPayload(); }
*/
/**
* Helper method to prevent nesting of previously thrown EFhirClientExceptions

View File

@ -32,11 +32,15 @@ package org.hl7.fhir.dstu2.utils.client;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import org.hl7.fhir.dstu2.model.Resource;
public class ResourceRequest<T extends Resource> {
@Getter
private T payload;
@Getter
private int httpStatus = -1;
@Getter
private String location;
private List<Integer> successfulStatuses = new ArrayList<Integer>();
private List<Integer> errorStatuses = new ArrayList<Integer>();
@ -67,14 +71,6 @@ public class ResourceRequest<T extends Resource> {
this.location = location;
}
public int getHttpStatus() {
return httpStatus;
}
public T getPayload() {
return payload;
}
public T getReference() {
T payloadResource = null;
if (payload != null) {
@ -99,7 +95,4 @@ public class ResourceRequest<T extends Resource> {
this.errorStatuses.add(status);
}
public String getLocation() {
return location;
}
}

View File

@ -0,0 +1,330 @@
package org.hl7.fhir.dstu2.utils.client;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.hl7.fhir.dstu2.formats.JsonParser;
import org.hl7.fhir.dstu2.model.*;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.http.HTTPHeader;
import org.hl7.fhir.utilities.http.HTTPHeaderUtil;
import org.hl7.fhir.utilities.http.HTTPRequest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
public class ClientUtilsTest {
public static final String DUMMY_LOCATION = "http://myhost/Patient/1";
public static final int TIMEOUT = 5000;
private MockWebServer server;
private HttpUrl serverUrl;
private ClientUtils clientUtils;
private final Address address = new Address()
.setCity("Toronto")
.setState("Ontario")
.setCountry("Canada");
private final HumanName humanName = new HumanName()
.addGiven("Mark")
.addFamily("Iantorno");
private final Patient patient = new Patient()
.addName(humanName)
.addAddress(address)
.setGender(Enumerations.AdministrativeGender.MALE);
@BeforeEach
void setup() {
setupMockServer();
clientUtils = new ClientUtils();
}
void setupMockServer() {
server = new MockWebServer();
serverUrl = server.url("/v1/endpoint");
}
@Test
@DisplayName("Test resource format headers are added correctly.")
void addResourceFormatHeadersGET() {
/* FIXME restore this after refactor
String testFormat = "yaml";
HTTPRequest request = new HTTPRequest().withUrl("http://www.google.com").withMethod(HTTPRequest.HttpMethod.GET);
Iterable<HTTPHeader> headers = FhirRequestBuilder.getResourceFormatHeaders(request, testFormat);
Map<String, List<String>> headersMap = HTTPHeaderUtil.getMultimap(headers);
Assertions.assertNotNull(headersMap.get("Accept"), "Accept header null.");
Assertions.assertEquals(testFormat, headersMap.get("Accept").get(0),
"Accept header not populated with expected value " + testFormat + ".");
Assertions.assertNull(headersMap.get("Content-Type"), "Content-Type header null.");
*/
}
@Test
@DisplayName("Test resource format headers are added correctly (POST).")
void addResourceFormatHeadersPOST() {
/*FIXME restore this after refactor
String testFormat = "yaml";
HTTPRequest request = new HTTPRequest().withUrl("http://www.google.com").withMethod(HTTPRequest.HttpMethod.POST);
Iterable<HTTPHeader> headers = FhirRequestBuilder.getResourceFormatHeaders(request, testFormat);
Map<String, List<String>> headersMap = HTTPHeaderUtil.getMultimap(headers);
Assertions.assertNotNull(headersMap.get("Accept"), "Accept header null.");
Assertions.assertEquals(testFormat, headersMap.get("Accept").get(0),
"Accept header not populated with expected value " + testFormat + ".");
Assertions.assertNotNull(headersMap.get("Content-Type"), "Content-Type header null.");
Assertions.assertEquals(testFormat + ";charset=" + FhirRequestBuilder.DEFAULT_CHARSET, headersMap.get("Content-Type").get(0),
"Content-Type header not populated with expected value \"" + testFormat + ";charset=" + FhirRequestBuilder.DEFAULT_CHARSET + "\".");
*/
}
@Test
public void testResourceFormat() {
ResourceFormat format = ResourceFormat.RESOURCE_XML;
assertThat(format.getHeader()).isEqualTo("application/xml+fhir");
format = ResourceFormat.RESOURCE_JSON;
assertThat(format.getHeader()).isEqualTo("application/json+fhir");
}
@Test
public void testResourceRequest() {
ResourceRequest<Patient> request = new ResourceRequest<>(new Patient(), 200, "location");
request.addSuccessStatus(200);
assertTrue(request.getPayload().equalsDeep(new Patient()));
assertThat(request.getHttpStatus()).isEqualTo(200);
assertThat(request.getLocation()).isEqualTo("location");
assertTrue(request.isSuccessfulRequest());
assertFalse(request.isUnsuccessfulRequest());
}
@Test
public void testIssueGetResourceRequest() throws IOException {
server.enqueue(
new MockResponse()
.setBody(new String(generateResourceBytes(patient)))
.addHeader("Content-Type", "application/json+fhir")
.addHeader("Location", DUMMY_LOCATION)
);
ResourceRequest<Patient> resourceRequest = clientUtils.issueGetResourceRequest(serverUrl.uri(), "application/json+fhir", TIMEOUT);
resourceRequest.addSuccessStatus(200);
assertThat(resourceRequest.getLocation()).isEqualTo(DUMMY_LOCATION);
assertTrue(resourceRequest.isSuccessfulRequest());
assertTrue(patient.equalsDeep(resourceRequest.getPayload()));
}
@Test
void testIssueGetResourceRequest_withContentLocation() throws IOException {
server.enqueue(
new MockResponse()
.setBody(new String(generateResourceBytes(patient)))
.addHeader("Content-Type", "application/json+fhir")
.addHeader("Content-Location", DUMMY_LOCATION)
);
ResourceRequest<Patient> resourceRequest = clientUtils.issueGetResourceRequest(serverUrl.uri(), "application/json+fhir", TIMEOUT);
resourceRequest.addSuccessStatus(200);
assertThat(resourceRequest.getLocation()).isEqualTo(DUMMY_LOCATION);
assertTrue(resourceRequest.isSuccessfulRequest());
assertTrue(patient.equalsDeep(resourceRequest.getPayload()));
}
@Test
@DisplayName("Test that getLocationHeader returns 'location' header when both 'location' and 'content-location' are set.")
void testIssueGetResourceRequest_ReturnsLocationHeaderWhenBothSet() throws IOException {
server.enqueue(
new MockResponse()
.setBody(new String(generateResourceBytes(patient)))
.addHeader("Content-Type", "application/json+fhir")
.addHeader("Location", DUMMY_LOCATION)
.addHeader("Content-Location", "Wrong wrong wrong")
);
ResourceRequest<Patient> resourceRequest = clientUtils.issueGetResourceRequest(serverUrl.uri(), "application/json+fhir", TIMEOUT);
resourceRequest.addSuccessStatus(200);
assertThat(resourceRequest.getLocation()).isEqualTo(DUMMY_LOCATION);
assertTrue(resourceRequest.isSuccessfulRequest());
assertTrue(patient.equalsDeep(resourceRequest.getPayload()));
}
@Test
@DisplayName("Test that getLocationHeader returns 'null' header when neither 'location' or 'content-location' are set.")
void testIssueGetResourceRequest_ReturnsNullWhenNoHeadersSet() throws IOException {
server.enqueue(
new MockResponse()
.setBody(new String(generateResourceBytes(patient)))
.addHeader("Content-Type", "application/json+fhir")
);
ResourceRequest<Patient> resourceRequest = clientUtils.issueGetResourceRequest(serverUrl.uri(), "application/json+fhir", TIMEOUT);
resourceRequest.addSuccessStatus(200);
assertThat(resourceRequest.getLocation()).isNull();
assertTrue(resourceRequest.isSuccessfulRequest());
assertTrue(patient.equalsDeep(resourceRequest.getPayload()));
}
@Test
public void testIssuePostRequest() throws IOException, InterruptedException {
byte[] payload = generateResourceBytes(patient);
server.enqueue(
new MockResponse().setBody(new String(payload)).addHeader("Content-Type", "application/json+fhir").addHeader("Location", DUMMY_LOCATION).setResponseCode(201)
);
ResourceRequest<Patient> resourceRequest = clientUtils.issuePostRequest(serverUrl.uri(), payload, "application/json+fhir", TIMEOUT);
resourceRequest.addSuccessStatus(201);
RecordedRequest recordedRequest = server.takeRequest();
assertArrayEquals(payload, recordedRequest.getBody().readByteArray(),
"PUT request payload does not match send data.");
assertThat(resourceRequest.getLocation()).isEqualTo(DUMMY_LOCATION);
assertTrue(resourceRequest.isSuccessfulRequest());
assertTrue(patient.equalsDeep(resourceRequest.getPayload()));
}
@Test
public void testIssuePutRequest() throws IOException, InterruptedException {
byte[] payload = generateResourceBytes(patient);
server.enqueue(
new MockResponse().setBody(new String(payload)).addHeader("Content-Type", "application/json+fhir").addHeader("Location", DUMMY_LOCATION).setResponseCode(200)
);
ResourceRequest<Patient> resourceRequest = clientUtils.issuePutRequest(serverUrl.uri(), payload, "application/json+fhir", TIMEOUT);
resourceRequest.addSuccessStatus(200);
RecordedRequest recordedRequest = server.takeRequest();
assertArrayEquals(payload, recordedRequest.getBody().readByteArray(),
"PUT request payload does not match send data.");
assertThat(resourceRequest.getLocation()).isEqualTo(DUMMY_LOCATION);
assertTrue(resourceRequest.isSuccessfulRequest());
assertTrue(patient.equalsDeep(resourceRequest.getPayload()));
}
@Test
public void testIssueDeleteRequest() throws IOException {
server.enqueue(
new MockResponse().addHeader("Location", DUMMY_LOCATION).setResponseCode(204)
);
boolean success = clientUtils.issueDeleteRequest(serverUrl.uri());
assertTrue(success);
}
@Test
public void testIssueDeleteRequest_fail() throws IOException, InterruptedException {
server.enqueue(
new MockResponse().addHeader("Location", DUMMY_LOCATION).setResponseCode(500)
);
boolean success = clientUtils.issueDeleteRequest(serverUrl.uri());
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod()).isEqualTo("DELETE");
assertThat(recordedRequest.getRequestUrl().uri()).isEqualTo(serverUrl.uri());
assertFalse(success);
}
@Test
@DisplayName("test logger works")
public void testLogger() throws IOException, InterruptedException {
byte[] payload = generateResourceBytes(patient);
server.enqueue(
new MockResponse()
.setResponseCode(200)
.setBody(new String(payload))
);
ToolingClientLogger mockLogger = Mockito.mock(ToolingClientLogger.class);
clientUtils.setLogger(mockLogger);
clientUtils.issuePostRequest(serverUrl.uri(), payload, "application/json+fhir"
, null, TIMEOUT);
server.takeRequest();
Mockito.verify(mockLogger, Mockito.times(1))
.logRequest(Mockito.anyString(), Mockito.anyString(), Mockito.anyList(), Mockito.any());
Mockito.verify(mockLogger, Mockito.times(1))
.logResponse(Mockito.anyString(), Mockito.anyList(), Mockito.any(), Mockito.anyLong());
}
@Test
@DisplayName("Test that FATAL issue severity triggers error.")
void hasErrorTestFatal() {
OperationOutcome outcome = new OperationOutcome();
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.INFORMATION));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.NULL));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.WARNING));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.FATAL));
Assertions.assertTrue(clientUtils.hasError(outcome), "Error check not triggered for FATAL issue severity.");
}
@Test
@DisplayName("Test that ERROR issue severity triggers error.")
void hasErrorTestError() {
OperationOutcome outcome = new OperationOutcome();
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.INFORMATION));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.NULL));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.WARNING));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.ERROR));
Assertions.assertTrue(clientUtils.hasError(outcome), "Error check not triggered for ERROR issue severity.");
}
@Test
@DisplayName("Test that no FATAL or ERROR issue severity does not trigger error.")
void hasErrorTestNoErrors() {
OperationOutcome outcome = new OperationOutcome();
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.INFORMATION));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.NULL));
outcome.addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setSeverity(OperationOutcome.IssueSeverity.WARNING));
Assertions.assertFalse(clientUtils.hasError(outcome), "Error check triggered unexpectedly.");
}
@Test
@DisplayName("GET request, test client retries after timeout failure.")
void test_get_retries_with_timeout() throws IOException, URISyntaxException {
int failedAttempts = new Random().nextInt(5) + 1;
System.out.println("Simulating <" + failedAttempts + "> failed connections (timeouts) before success.");
for (int i = 0; i < failedAttempts; i++) {
server.enqueue(new MockResponse().setHeadersDelay(TIMEOUT * 10, TimeUnit.MILLISECONDS)
.setBody(new String(generateResourceBytes(patient))));
}
server.enqueue(new MockResponse().setBody(new String(generateResourceBytes(patient))));
clientUtils.setRetryCount(failedAttempts + 1);
ResourceRequest<Resource> resourceRequest = clientUtils.issueGetResourceRequest(serverUrl.uri(), "application/json+fhir"
, TIMEOUT);
Assertions.assertTrue(resourceRequest.isSuccessfulRequest());
Assertions.assertTrue(patient.equalsDeep(resourceRequest.getPayload()),
"GET request returned resource does not match expected.");
}
@Test
@DisplayName("GET request, test client retries after bad response.")
void test_get_retries_with_unsuccessful_response() throws IOException, URISyntaxException {
int failedAttempts = new Random().nextInt(5) + 1;
System.out.println("Simulating <" + failedAttempts + "> failed connections (bad response codes) before success.");
for (int i = 0; i < failedAttempts; i++) {
server.enqueue(new MockResponse().setResponseCode(400 + i).setBody(new String(generateResourceBytes(patient))));
}
server.enqueue(new MockResponse().setBody(new String(generateResourceBytes(patient))));
clientUtils.setRetryCount(failedAttempts + 1);
ResourceRequest<Resource> resourceRequest = clientUtils.issueGetResourceRequest(serverUrl.uri(), "application/json+fhir", TIMEOUT);
Assertions.assertTrue(resourceRequest.isSuccessfulRequest());
Assertions.assertTrue(patient.equalsDeep(resourceRequest.getPayload()),
"GET request returned resource does not match expected.");
}
byte[] generateResourceBytes(Resource resource) throws IOException {
return new JsonParser().composeBytes(resource);
}
}