diff --git a/pom.xml b/pom.xml
index a3ae0a9..f40a04a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,7 @@
UTF-8
- 2.8.2
+ 2.8.8
1.0.6
@@ -62,53 +62,28 @@
org.apache.commons
commons-lang3
- 3.4
+ 3.5
org.springframework
spring-core
- 4.2.5.RELEASE
+ 4.3.8.RELEASE
com.flipkart.zjsonpatch
zjsonpatch
- 0.2.1
+ 0.3.1
- com.google.guava
- guava
- 19.0
-
-
- com.mashape.unirest
- unirest-java
- 1.4.5
-
-
- org.jglue.fluent-json
- fluent-json
- 2.0.0
+ org.apache.httpcomponents
+ httpclient
+ 4.5.3
com.github.fge
json-schema-validator
2.2.6
-
- junit
- junit
- 4.12
-
-
- com.samskivert
- jmustache
- 1.13
-
-
- org.assertj
- assertj-core
- 3.4.1
-
com.fasterxml.jackson.core
jackson-core
@@ -124,11 +99,23 @@
jackson-dataformat-yaml
${jackson.version}
+
+ com.samskivert
+ jmustache
+ 1.13
+ true
+
+
+ junit
+ junit
+ 4.12
+ test
+
org.codehaus.groovy
groovy-all
- 2.4.6
+ 2.4.10
test
@@ -143,6 +130,12 @@
${jackson.version}
test
+
+ org.assertj
+ assertj-core
+ 3.6.2
+ test
+
diff --git a/src/main/java/rocks/bastion/core/FileRequest.java b/src/main/java/rocks/bastion/core/FileRequest.java
index 1d6fa88..a84406b 100644
--- a/src/main/java/rocks/bastion/core/FileRequest.java
+++ b/src/main/java/rocks/bastion/core/FileRequest.java
@@ -17,7 +17,7 @@
/**
* An HTTP request which takes any arbitrary file/resource, using the data within as its content body. The {@linkplain FileRequest} will not perform
* any conversions or validation on any user-supplied body content. Use the static factory methods, such as {@link #post(String, String)}
- * or {@link #delete(String, String)} to initialise a new {@linkplain FileRequest}.
+ * or {@link #patch(String, String)} to initialise a new {@linkplain FileRequest}.
*
* By default, this request will contain no headers (except for the content-type) and no query parameters. Use the {@link #addHeader(String, String)}
* and {@link #addQueryParam(String, String)}} to add them. Also, initially, Bastion will attempt to guess the MIME type to send as part of the
@@ -54,31 +54,6 @@ public static FileRequest post(String url, String resource) throws UnreadableRes
return new FileRequest(HttpMethod.POST, url, resource);
}
- /**
- * Construct an HTTP request, using the DELETE method, to be sent on the specified URL. The request's content will be loaded
- * from the specified resource URL. Bastion will attempt to guess the MIME type to send by looking at the given file.
- *
- * The resource source is specified as a resource URL as described in {@link ResourceLoader}. Valid resource URLs include (but
- * are not limited to):
- *
- *
- * - {@code classpath:/rocks/bastion/json/Sushi.json}
- * - {@code file:/home/user/Sushi.json}
- *
- *
- * For more information about which resource URLs are accepted see the documentation for {@link ResourceLoader}.
- *
- *
- * @param url A non-{@literal null} URL to send the request on
- * @param resource A non-{@literal null} resource URL to load the data from, for this request
- * @return An HTTP request using the DELETE method
- * @throws UnreadableResourceException Thrown if the specified resource exists but cannot be read (because it is a directory, for example)
- * @throws ResourceNotFoundException Thrown if the specified resource does not exist
- */
- public static FileRequest delete(String url, String resource) throws UnreadableResourceException, ResourceNotFoundException {
- return new FileRequest(HttpMethod.DELETE, url, resource);
- }
-
/**
* Construct an HTTP request, using the PUT method, to be sent on the specified URL. The request's content will be loaded
* from the specified resource URL. Bastion will attempt to guess the MIME type to send by looking at the given file.
diff --git a/src/main/java/rocks/bastion/core/RequestExecutor.java b/src/main/java/rocks/bastion/core/RequestExecutor.java
index 3641c2c..a400b16 100644
--- a/src/main/java/rocks/bastion/core/RequestExecutor.java
+++ b/src/main/java/rocks/bastion/core/RequestExecutor.java
@@ -1,13 +1,27 @@
package rocks.bastion.core;
-import com.mashape.unirest.http.HttpResponse;
-import com.mashape.unirest.http.Unirest;
-import com.mashape.unirest.http.exceptions.UnirestException;
-import com.mashape.unirest.request.HttpRequestWithBody;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import rocks.bastion.core.configuration.Configuration;
-import java.io.InputStream;
+import java.io.IOException;
import java.net.SocketTimeoutException;
+import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
@@ -15,8 +29,6 @@
import java.util.Objects;
import java.util.stream.Collectors;
-import rocks.bastion.core.configuration.Configuration;
-
/**
* Responsible for executing a Bastion remote request built using the {@link BastionBuilderImpl} builder and prepare a response object.
*/
@@ -24,19 +36,24 @@ public class RequestExecutor {
private Configuration configuration;
private HttpRequest bastionHttpRequest;
- private com.mashape.unirest.request.HttpRequest executableHttpRequest;
+ private HttpUriRequest executableHttpRequest;
private Collection headers;
private String resolvedUrl;
+ private URIBuilder uriBuilder;
public RequestExecutor(HttpRequest bastionHttpRequest, Configuration configuration) {
- Objects.requireNonNull(bastionHttpRequest);
- this.bastionHttpRequest = bastionHttpRequest;
- this.configuration = configuration;
- executableHttpRequest = prepareHttpRequest();
- applyHeaders();
- applyQueryParameters();
- applyRouteParameters();
- applyBody();
+ try {
+ Objects.requireNonNull(bastionHttpRequest);
+ this.bastionHttpRequest = bastionHttpRequest;
+ this.configuration = configuration;
+ uriBuilder = new URIBuilder(applyRouteParameters());
+ applyQueryParameters();
+ executableHttpRequest = prepareHttpRequest();
+ applyHeaders();
+ applyBody();
+ } catch (URISyntaxException exception) {
+ throw new IllegalStateException("Could not compute URI", exception);
+ }
}
public String getMethod() {
@@ -58,96 +75,109 @@ public Collection getHeaders() {
*/
public Response execute() {
try {
- HttpResponse httpResponse = performRequest();
+ CloseableHttpResponse httpResponse = performRequest();
return convertToRawResponse(httpResponse);
- } catch (UnirestException exception) {
- if (exception.getCause() instanceof SocketTimeoutException) {
- throw new AssertionError(String.format("Failed to receive response before timeout of [%s] ms", resolveTimeoutOrFallbackToGlobal(bastionHttpRequest, configuration)));
- }
+ } catch (SocketTimeoutException exception) {
+ throw new AssertionError(String.format("Failed to receive response before timeout of [%s] ms",
+ resolveTimeoutOrFallbackToGlobal(bastionHttpRequest, configuration)), exception);
+ } catch (IOException exception) {
throw new IllegalStateException("Failed executing request", exception);
}
}
- private com.mashape.unirest.request.HttpRequest prepareHttpRequest() {
+ private HttpUriRequest prepareHttpRequest() throws URISyntaxException {
long timeout = resolveTimeoutOrFallbackToGlobal(bastionHttpRequest, configuration);
- Unirest.setTimeouts(timeout, timeout);
- com.mashape.unirest.request.HttpRequest request;
+ HttpRequestBase request;
+ resolvedUrl = uriBuilder.build().toString();
switch (bastionHttpRequest.method().getValue()) {
case "GET":
- request = Unirest.get(bastionHttpRequest.url());
+ request = new HttpGet(resolvedUrl);
break;
case "POST":
- request = Unirest.post(bastionHttpRequest.url());
+ request = new HttpPost(resolvedUrl);
break;
case "PATCH":
- request = Unirest.patch(bastionHttpRequest.url());
+ request = new HttpPost(resolvedUrl);
break;
case "DELETE":
- request = Unirest.delete(bastionHttpRequest.url());
+ request = new HttpDelete(resolvedUrl);
break;
case "PUT":
- request = Unirest.put(bastionHttpRequest.url());
+ request = new HttpPut(resolvedUrl);
break;
case "OPTIONS":
- request = Unirest.options(bastionHttpRequest.url());
+ request = new HttpOptions(resolvedUrl);
break;
case "HEAD":
- request = Unirest.head(bastionHttpRequest.url());
+ request = new HttpHead(resolvedUrl);
break;
default:
- throw new UnsupportedOperationException(String.format("We cannot perform a request of type %s.", bastionHttpRequest.method().getValue()));
+ throw new UnsupportedOperationException(String.format("We cannot perform a request of type %s.",
+ bastionHttpRequest.method().getValue()));
}
+ request.setConfig(RequestConfig.custom()
+ .setConnectTimeout((int) timeout)
+ .setSocketTimeout((int) timeout)
+ .setConnectionRequestTimeout((int) timeout)
+ .build());
return request;
}
- private static long resolveTimeoutOrFallbackToGlobal(HttpRequest request, Configuration configuration) {
- if (request.timeout() == HttpRequest.USE_GLOBAL_TIMEOUT) {
- return configuration.getGlobalRequestAttributes().getGlobalRequestTimeout();
- } else {
- return request.timeout();
- }
- }
-
private void applyHeaders() {
headers = new LinkedList<>(configuration.getGlobalRequestAttributes().getGlobalHeaders());
headers.addAll(bastionHttpRequest.headers());
- if (headers.stream().noneMatch(header -> header.getName().equalsIgnoreCase("content-type")) && bastionHttpRequest.contentType().isPresent()) {
+ if (headers.stream().noneMatch(header -> header.getName().equalsIgnoreCase("content-type")) && bastionHttpRequest.contentType()
+ .isPresent()) {
headers.add(new ApiHeader("Content-Type", bastionHttpRequest.contentType().get().toString()));
}
- headers.forEach(header -> executableHttpRequest.header(header.getName(), header.getValue()));
+ headers.forEach(header -> executableHttpRequest.addHeader(header.getName(), header.getValue()));
}
private void applyQueryParameters() {
List apiQueryParams = new ArrayList<>(configuration.getGlobalRequestAttributes().getGlobalQueryParams());
apiQueryParams.addAll(bastionHttpRequest.queryParams());
- apiQueryParams.forEach(queryParam -> executableHttpRequest.queryString(queryParam.getName(), queryParam.getValue()));
- resolvedUrl = executableHttpRequest.getUrl();
+ apiQueryParams.forEach(queryParam -> uriBuilder.addParameter(queryParam.getName(), queryParam.getValue()));
}
- private void applyRouteParameters() {
+ private String applyRouteParameters() {
List routeParams = new ArrayList<>(configuration.getGlobalRequestAttributes().getGlobalRouteParams());
routeParams.addAll(bastionHttpRequest.routeParams());
- routeParams.forEach(routeParam -> executableHttpRequest.routeParam(routeParam.getName(), routeParam.getValue()));
- resolvedUrl = executableHttpRequest.getUrl();
+ String urlWithPlaceholders = bastionHttpRequest.url();
+ for (RouteParam routeParam : routeParams) {
+ urlWithPlaceholders = urlWithPlaceholders.replaceAll("\\Q{" + routeParam.getName() + "}\\E", routeParam.getValue());
+ }
+ return urlWithPlaceholders;
}
private void applyBody() {
- if (executableHttpRequest instanceof HttpRequestWithBody) {
- ((HttpRequestWithBody) executableHttpRequest).body(bastionHttpRequest.body().toString());
+ if (executableHttpRequest instanceof HttpEntityEnclosingRequest) {
+ ((HttpEntityEnclosingRequest) executableHttpRequest).setEntity(new StringEntity(
+ bastionHttpRequest.body().toString(),
+ bastionHttpRequest.contentType().orElse(null)
+ ));
}
}
- private HttpResponse performRequest() throws UnirestException {
- return executableHttpRequest.asBinary();
+ private CloseableHttpResponse performRequest() throws IOException {
+ CloseableHttpClient httpClient = HttpClients.createDefault();
+ return httpClient.execute(executableHttpRequest);
}
- private Response convertToRawResponse(HttpResponse httpResponse) {
- return new RawResponse(httpResponse.getStatus(),
- httpResponse.getStatusText(),
- httpResponse.getHeaders().entrySet().stream().flatMap(header ->
- header.getValue().stream().map(headerValue ->
- new ApiHeader(header.getKey(), headerValue))).collect(Collectors.toList()),
- httpResponse.getBody());
+ private Response convertToRawResponse(CloseableHttpResponse httpResponse) throws IOException {
+ return new RawResponse(httpResponse.getStatusLine().getStatusCode(),
+ httpResponse.getStatusLine().getReasonPhrase(),
+ Arrays.stream(httpResponse.getAllHeaders())
+ .map(header -> new ApiHeader(header.getName(), header.getValue()))
+ .collect(Collectors.toList()),
+ httpResponse.getEntity().getContent());
+ }
+
+ private static long resolveTimeoutOrFallbackToGlobal(HttpRequest request, Configuration configuration) {
+ if (request.timeout() == HttpRequest.USE_GLOBAL_TIMEOUT) {
+ return configuration.getGlobalRequestAttributes().getGlobalRequestTimeout();
+ } else {
+ return request.timeout();
+ }
}
}
diff --git a/src/main/java/rocks/bastion/core/json/JsonRequest.java b/src/main/java/rocks/bastion/core/json/JsonRequest.java
index 30c280f..035af0c 100644
--- a/src/main/java/rocks/bastion/core/json/JsonRequest.java
+++ b/src/main/java/rocks/bastion/core/json/JsonRequest.java
@@ -1,13 +1,21 @@
package rocks.bastion.core.json;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.entity.ContentType;
-import rocks.bastion.core.*;
+import rocks.bastion.core.ApiHeader;
+import rocks.bastion.core.ApiQueryParam;
+import rocks.bastion.core.CommonRequestAttributes;
+import rocks.bastion.core.HttpMethod;
+import rocks.bastion.core.HttpRequest;
+import rocks.bastion.core.RouteParam;
+import rocks.bastion.core.TemplateCompilationException;
+import rocks.bastion.core.TemplateContentCompiler;
import rocks.bastion.core.resource.ResourceLoader;
import rocks.bastion.core.resource.ResourceNotFoundException;
import rocks.bastion.core.resource.UnreadableResourceException;
+import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
@@ -264,6 +272,17 @@ public static JsonRequest deleteFromResource(String url, String jsonSource) thro
* the request will have the "application/json" HTTP header and no other additional headers and no query parameters.
* It will also have a descriptive name which is generated by combining the HTTP method with the URL.
*
+ *
+ * IMPORTANT: Before you can use this method, the JMustache library MUST BE on your classpath. If you're using Maven,
+ * you should add the following to your POM file:
+ *
+ * <dependency>
+ * <groupId>com.samskivert</groupId>
+ * <artifactId>jmustache</artifactId>
+ * <version>1.13</version>
+ * </dependency>
+ *
+ *
*
* @param method The HTTP method to use for this request
* @param url The URL to send this request on
@@ -304,6 +323,17 @@ public static JsonRequest fromTemplate(HttpMethod method, String url, String jso
* the request will have the "application/json" HTTP header and no other additional headers and no query parameters.
* It will also have a descriptive name which is generated by combining the HTTP method with the URL.
*
+ *
+ * IMPORTANT: Before you can use this method, the JMustache library MUST BE on your classpath. If you're using Maven,
+ * you should add the following to your POM file:
+ *
+ * <dependency>
+ * <groupId>com.samskivert</groupId>
+ * <artifactId>jmustache</artifactId>
+ * <version>1.13</version>
+ * </dependency>
+ *
+ *
*
* @param url The URL to send this request on
* @param jsonTemplateSource The resource URL to load the template file from, for this request
@@ -341,6 +371,17 @@ public static JsonRequest postFromTemplate(String url, String jsonTemplateSource
* the request will have the "application/json" HTTP header and no other additional headers and no query parameters.
* It will also have a descriptive name which is generated by combining the HTTP method with the URL.
*
+ *
+ * IMPORTANT: Before you can use this method, the JMustache library MUST BE on your classpath. If you're using Maven,
+ * you should add the following to your POM file:
+ *
+ * <dependency>
+ * <groupId>com.samskivert</groupId>
+ * <artifactId>jmustache</artifactId>
+ * <version>1.13</version>
+ * </dependency>
+ *
+ *
*
* @param url The URL to send this request on
* @param jsonTemplateSource The resource URL to load the template file from, for this request
@@ -378,6 +419,17 @@ public static JsonRequest putFromTemplate(String url, String jsonTemplateSource,
* the request will have the "application/json" HTTP header and no other additional headers and no query parameters.
* It will also have a descriptive name which is generated by combining the HTTP method with the URL.
*
+ *
+ * IMPORTANT: Before you can use this method, the JMustache library MUST BE on your classpath. If you're using Maven,
+ * you should add the following to your POM file:
+ *
+ * <dependency>
+ * <groupId>com.samskivert</groupId>
+ * <artifactId>jmustache</artifactId>
+ * <version>1.13</version>
+ * </dependency>
+ *
+ *
*
* @param url The URL to send this request on
* @param jsonTemplateSource The resource URL to load the template file from, for this request
@@ -415,6 +467,17 @@ public static JsonRequest patchFromTemplate(String url, String jsonTemplateSourc
* the request will have the "application/json" HTTP header and no other additional headers and no query parameters.
* It will also have a descriptive name which is generated by combining the HTTP method with the URL.
*
+ *
+ * IMPORTANT: Before you can use this method, the JMustache library MUST BE on your classpath. If you're using Maven,
+ * you should add the following to your POM file:
+ *
+ * <dependency>
+ * <groupId>com.samskivert</groupId>
+ * <artifactId>jmustache</artifactId>
+ * <version>1.13</version>
+ * </dependency>
+ *
+ *
*
* @param url The URL to send this request on
* @param jsonTemplateSource The resource URL to load the template file from, for this request
@@ -628,9 +691,11 @@ public JsonRequest setTimeout(long timeout) {
private void validateJson() throws InvalidJsonException {
String jsonBody = requestAttributes.body().toString();
try {
- new JsonParser().parse(jsonBody);
- } catch (JsonSyntaxException parseException) {
+ new ObjectMapper().getFactory().createParser(jsonBody).readValueAsTree();
+ } catch (JsonParseException parseException) {
throw new InvalidJsonException(parseException, jsonBody);
+ } catch (IOException ioException) {
+ throw new RuntimeException("Unexpected error occurred parsing JSON", ioException);
}
}
}
diff --git a/src/main/java/rocks/bastion/core/json/JsonResponseAssertions.java b/src/main/java/rocks/bastion/core/json/JsonResponseAssertions.java
index f006411..4196330 100644
--- a/src/main/java/rocks/bastion/core/json/JsonResponseAssertions.java
+++ b/src/main/java/rocks/bastion/core/json/JsonResponseAssertions.java
@@ -1,20 +1,27 @@
package rocks.bastion.core.json;
import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flipkart.zjsonpatch.JsonDiff;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
import org.apache.http.entity.ContentType;
-import org.junit.Assert;
-import rocks.bastion.core.*;
+import rocks.bastion.core.Assertions;
+import rocks.bastion.core.ModelResponse;
+import rocks.bastion.core.Response;
+import rocks.bastion.core.TemplateCompilationException;
+import rocks.bastion.core.TemplateContentCompiler;
import rocks.bastion.core.resource.ResourceLoader;
import rocks.bastion.core.resource.ResourceNotFoundException;
import rocks.bastion.core.resource.UnreadableResourceException;
import java.io.IOException;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
import static java.lang.String.format;
@@ -91,6 +98,17 @@ public static JsonResponseAssertions fromResource(int expectedStatusCode, String
* The loaded JSON, after variables are resolved, must be syntactically correct; otherwise, an exception is
* thrown to indicate that the expected JSON string is invalid.
*
+ *
+ * IMPORTANT: Before you can use this method, the JMustache library MUST BE on your classpath. If you're using Maven,
+ * you should add the following to your POM file:
+ *
+ * <dependency>
+ * <groupId>com.samskivert</groupId>
+ * <artifactId>jmustache</artifactId>
+ * <version>1.13</version>
+ * </dependency>
+ *
+ *
*
* @param expectedStatusCode The expected HTTP status code
* @param expectedJsonSource The resource to load the expected JSON object template from
@@ -200,7 +218,9 @@ public JsonResponseAssertions overrideContentType(ContentType contentType) {
@Override
public void execute(int statusCode, ModelResponse> response, Object model) throws AssertionError {
try {
- Assert.assertEquals("Response Status Code", expectedStatusCode, statusCode);
+ if (expectedStatusCode != statusCode) {
+ throw new AssertionError(format("Received status code <%d> is not as expected <%d>", statusCode, expectedStatusCode));
+ }
assertContentTypeHeader(response);
JsonNode jsonPatch = computeJsonPatch(response);
assertJsonPatchIsEmpty(jsonPatch);
@@ -211,7 +231,7 @@ public void execute(int statusCode, ModelResponse> response, Object model) thr
private static void assertJsonPatchIsEmpty(JsonNode jsonPatch) {
if (jsonPatch.size() != 0) {
- Assert.fail(format("Actual response body is not as expected. The following JSON Patch (as per RFC-6902) tells you what operations you need " +
+ throw new AssertionError(format("Actual response body is not as expected. The following JSON Patch (as per RFC-6902) tells you what operations you need " +
"to perform to transform the actual response body into the expected response body:\n %s", jsonPatch.toString()));
}
}
@@ -235,15 +255,19 @@ private String sanitizePropertyName(String field) {
private void validateExpectedJson() throws InvalidJsonException {
try {
- new JsonParser().parse(expectedJson);
+ new ObjectMapper().getFactory().createParser(expectedJson).readValueAsTree();
} catch (JsonParseException parseException) {
throw new InvalidJsonException(parseException, expectedJson);
+ } catch (IOException ioException) {
+ throw new RuntimeException("Unexpected error occurred parsing JSON", ioException);
}
}
private void assertContentTypeHeader(Response response) {
- Assert.assertTrue("Content-type exists in response", response.getContentType().isPresent());
- Assert.assertEquals("Content-type MIME type", contentType.getMimeType(), response.getContentType().get().getMimeType());
+ ContentType contentType = response.getContentType().orElseThrow(() -> new AssertionError("Content-type expected to be in response"));
+ if (!Objects.equals(contentType.getMimeType(), this.contentType.getMimeType())) {
+ throw new AssertionError(format("Expected content-type header <%s> to be as expected <%s>", contentType.getMimeType(), this.contentType.getMimeType()));
+ }
}
private JsonNode computeJsonPatch(Response response) throws IOException {
diff --git a/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java b/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java
index 530c389..9692c15 100644
--- a/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java
+++ b/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java
@@ -9,7 +9,6 @@
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import org.apache.http.entity.ContentType;
-import org.junit.Assert;
import rocks.bastion.core.Assertions;
import rocks.bastion.core.ModelResponse;
import rocks.bastion.core.Response;
@@ -20,6 +19,8 @@
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
+import static java.lang.String.format;
+
/**
* Asserts that an API response conforms to a given JSON schema.
*/
@@ -59,7 +60,7 @@ public void execute(int statusCode,
throw new RuntimeException("An unknown error occurred while processing the JSON schema and API response", e);
}
}
-
+
/**
* The assertions object will initially check that the content-type header returned by the actual response is
* "application/json". This can be overriden to check for a different content-type header using this method. Despite
@@ -82,12 +83,14 @@ private JsonNode convertResponseToJsonNode(ModelResponse> response) throws IOE
private void assertResponseConformsToSchema(JsonNode response) throws ProcessingException, IOException {
ProcessingReport validationReport = JsonSchemaFactory.byDefault()
- .getJsonSchema(getExpectedSchema()).validate(response);
+ .getJsonSchema(getExpectedSchema()).validate(response);
if (!validationReport.isSuccess()) {
String messages = StreamSupport.stream(validationReport.spliterator(), false)
- .map(ProcessingMessage::getMessage)
- .collect(Collectors.joining(", "));
- Assert.fail(String.format("Actual response body is not as specified. The following message(s) where produced during validation; %s.", messages));
+ .map(ProcessingMessage::getMessage)
+ .collect(Collectors.joining(", "));
+ throw new AssertionError(format(
+ "Actual response body is not as specified. The following message(s) where produced during validation; %s.",
+ messages));
}
}
@@ -97,8 +100,12 @@ private JsonNode getExpectedSchema() throws IOException {
}
private void assertContentTypeHeader(Response response) {
- Assert.assertTrue("Content-type exists in response", response.getContentType().isPresent());
- Assert.assertEquals("Content-type MIME type", contentType.getMimeType(), response.getContentType().get().getMimeType());
+ if (!response.getContentType().isPresent()) {
+ throw new AssertionError("Response content-type should not be missing.");
+ }
+ if (!response.getContentType().get().getMimeType().equals(contentType.getMimeType())) {
+ throw new AssertionError(format("Response content-type should be \"%s\" but got \"%s\" instead", contentType.getMimeType(),
+ response.getContentType().get().getMimeType()));
+ }
}
-
}
diff --git a/src/test/java/rocks/bastion/core/FileRequestTest.java b/src/test/java/rocks/bastion/core/FileRequestTest.java
index 984fcd6..1515066 100644
--- a/src/test/java/rocks/bastion/core/FileRequestTest.java
+++ b/src/test/java/rocks/bastion/core/FileRequestTest.java
@@ -26,14 +26,6 @@ public void put() throws Exception {
.call();
}
- @Test
- public void delete() throws Exception {
- Bastion.request("Create Sushi", FileRequest.delete("http://localhost:9876/sushi", "classpath:/json/create_sushi_request.json"))
- .bind(Sushi.class)
- .withAssertions(JsonResponseAssertions.fromResource(201, "classpath:/json/create_sushi_response.json").ignoreValuesForProperties("/id"))
- .call();
- }
-
@Test
public void patch() throws Exception {
Bastion.request("Create Sushi", FileRequest.patch("http://localhost:9876/sushi", "classpath:/json/create_sushi_request.json"))
diff --git a/src/test/java/rocks/bastion/core/assertions/JsonResponseAssertionsTest.java b/src/test/java/rocks/bastion/core/assertions/JsonResponseAssertionsTest.java
index 8c42e04..d49130d 100644
--- a/src/test/java/rocks/bastion/core/assertions/JsonResponseAssertionsTest.java
+++ b/src/test/java/rocks/bastion/core/assertions/JsonResponseAssertionsTest.java
@@ -63,7 +63,7 @@ public void execute_notIgnoredOrderForArrayFieldDisorderedAssertion_shouldThrowE
} catch (AssertionError assertionError) {
Assert.assertEquals("Assertion Failed Message", assertionError.getMessage(), "Actual response body is not as expected. The following JSON Patch (as per RFC-6902) tells you what operations you need to perform to transform the actual response body into the expected response body:" +
"\n" +
- " [{\"op\":\"move\",\"path\":\"/array/2\",\"from\":\"/array/0\"}]");
+ " [{\"op\":\"move\",\"from\":\"/array/0\",\"path\":\"/array/2\"}]");
return;
}
Assert.fail("An assertion error should have been thrown by the JSON Response Assertions");
@@ -93,7 +93,7 @@ public void execute_ignoredOrderForArrayFieldExtraElement_shouldThrowErrorWithDi
} catch (AssertionError assertionError) {
Assert.assertEquals("Assertion Failed Message", assertionError.getMessage(), "Actual response body is not as expected. The following JSON Patch (as per RFC-6902) tells you what operations you need to perform to transform the actual response body into the expected response body:" +
"\n" +
- " [{\"op\":\"remove\",\"path\":\"/array/0\"},{\"op\":\"replace\",\"path\":\"/array/2\",\"value\":\"third\"}]");
+ " [{\"op\":\"remove\",\"path\":\"/array/0\",\"value\":\"third\"},{\"op\":\"replace\",\"path\":\"/array/2\",\"value\":\"third\"}]");
return;
}
Assert.fail("An assertion error should have been thrown by the JSON Response Assertions");
@@ -108,7 +108,7 @@ public void execute_fromStringJsonMismatches_shouldThrowErrorWithDiff() throws E
} catch (AssertionError assertionError) {
Assert.assertEquals("Assertion Failed Message", assertionError.getMessage(), "Actual response body is not as expected. The following JSON Patch (as per RFC-6902) tells you what operations you need to perform to transform the actual response body into the expected response body:" +
"\n" +
- " [{\"op\":\"replace\",\"path\":\"/key\",\"value\":\"kyle\"},{\"op\":\"remove\",\"path\":\"/array\"}]");
+ " [{\"op\":\"replace\",\"path\":\"/key\",\"value\":\"kyle\"},{\"op\":\"remove\",\"path\":\"/array\",\"value\":[1,2]}]");
return;
}
Assert.fail("An assertion error should have been thrown by the JSON Response Assertions");
@@ -131,7 +131,7 @@ public void execute_fromFileJsonMismatches_shouldThrowErrorWithDiff() throws Exc
assertions.execute(200, response, response.getModel());
} catch (AssertionError assertionError) {
Assert.assertEquals("Assertion Failed Message", assertionError.getMessage(), "Actual response body is not as expected. The following JSON Patch (as per RFC-6902) tells you what operations you need to perform to transform the actual response body into the expected response body:\n" +
- " [{\"op\":\"move\",\"path\":\"/timestamp\",\"from\":\"/timestamp1\"},{\"op\":\"remove\",\"path\":\"/colours\"},{\"op\":\"remove\",\"path\":\"/favourites/country\"},{\"op\":\"add\",\"path\":\"/favourites/colours\",\"value\":[\"blue\",\"red\"]}]");
+ " [{\"op\":\"move\",\"from\":\"/timestamp1\",\"path\":\"/timestamp\"},{\"op\":\"remove\",\"path\":\"/colours\",\"value\":[\"blue\"]},{\"op\":\"remove\",\"path\":\"/favourites/country\",\"value\":\"Spain\"},{\"op\":\"add\",\"path\":\"/favourites/colours\",\"value\":[\"blue\",\"red\"]}]");
return;
}
Assert.fail("An assertion error should have been thrown by the JSON Response Assertions");
@@ -156,7 +156,8 @@ public void execute_fromTemplateJsonMismatches_shouldThrowErrorWithDiff() throws
assertions.execute(200, response, response.getModel());
} catch (AssertionError assertionError) {
Assert.assertEquals("Assertion Failed Message", assertionError.getMessage(), "Actual response body is not as expected. The following JSON Patch (as per RFC-6902) tells you what operations you need to perform to transform the actual response body into the expected response body:\n" +
- " [{\"op\":\"move\",\"path\":\"/timestamp\",\"from\":\"/timestamp1\"},{\"op\":\"remove\",\"path\":\"/colours\"},{\"op\":\"remove\",\"path\":\"/favourites/country\"},{\"op\":\"add\",\"path\":\"/favourites/colours\",\"value\":[\"blue\",\"red\"]}]");
+ " [{\"op\":\"move\",\"from\":\"/timestamp1\",\"path\":\"/timestamp\"},{\"op\":\"remove\",\"path\":\"/colours\",\"value\":[\"blue\"]}," +
+ "{\"op\":\"remove\",\"path\":\"/favourites/country\",\"value\":\"Spain\"},{\"op\":\"add\",\"path\":\"/favourites/colours\",\"value\":[\"blue\",\"red\"]}]");
return;
}
Assert.fail("An assertion error should have been thrown by the JSON Response Assertions");
diff --git a/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java b/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java
index d100cd8..4e034e2 100644
--- a/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java
+++ b/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java
@@ -55,7 +55,7 @@ public void execute_fromStringContentTypeMismatch_assertionErrorShouldBeThrown()
assertions.execute(201, response, response.getModel());
} catch (AssertionError assertionError) {
Assert.assertEquals("Assertion Failed Message",
- "Content-type MIME type expected:<[application/jso]n> but was:<[text/plai]n>",
+ "Response content-type should be \"application/json\" but got \"text/plain\" instead",
assertionError.getMessage());
return;
}
diff --git a/src/test/java/rocks/bastion/support/CreateSushiRequest.java b/src/test/java/rocks/bastion/support/CreateSushiRequest.java
index ee932e7..2993dd7 100644
--- a/src/test/java/rocks/bastion/support/CreateSushiRequest.java
+++ b/src/test/java/rocks/bastion/support/CreateSushiRequest.java
@@ -1,8 +1,13 @@
package rocks.bastion.support;
-import com.google.gson.Gson;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.entity.ContentType;
-import rocks.bastion.core.*;
+import rocks.bastion.core.ApiHeader;
+import rocks.bastion.core.ApiQueryParam;
+import rocks.bastion.core.HttpMethod;
+import rocks.bastion.core.HttpRequest;
+import rocks.bastion.core.RouteParam;
import rocks.bastion.support.embedded.Sushi;
import java.util.Collection;
@@ -48,7 +53,11 @@ public Collection routeParams() {
@Override
public Object body() {
- return new Gson().toJson(Sushi.newSushi().name("happiness").build());
+ try {
+ return new ObjectMapper().writeValueAsString(Sushi.newSushi().name("happiness").build());
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
}
}
diff --git a/src/test/java/rocks/bastion/support/embedded/Sushi.java b/src/test/java/rocks/bastion/support/embedded/Sushi.java
index 4e51a05..3ed7659 100644
--- a/src/test/java/rocks/bastion/support/embedded/Sushi.java
+++ b/src/test/java/rocks/bastion/support/embedded/Sushi.java
@@ -1,5 +1,7 @@
package rocks.bastion.support.embedded;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
import java.math.BigDecimal;
/**
@@ -14,6 +16,7 @@ public static Builder newSushi() {
private long id;
private String name;
private BigDecimal price;
+ @JsonInclude(JsonInclude.Include.NON_NULL)
private Type type;
protected Sushi() {
diff --git a/src/test/java/rocks/bastion/support/embedded/SushiService.java b/src/test/java/rocks/bastion/support/embedded/SushiService.java
index 27d1bc0..eb7d428 100644
--- a/src/test/java/rocks/bastion/support/embedded/SushiService.java
+++ b/src/test/java/rocks/bastion/support/embedded/SushiService.java
@@ -1,9 +1,13 @@
package rocks.bastion.support.embedded;
-import com.google.gson.Gson;
-import com.google.gson.JsonParseException;
-import spark.*;
-
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import spark.ResponseTransformer;
+import spark.Route;
+import spark.Spark;
+
+import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@@ -146,15 +150,23 @@ public void stop() {
*/
private class JsonTransformer implements ResponseTransformer {
- private Gson gson = new Gson();
+ private ObjectMapper objectMapper = new ObjectMapper();
@Override
public String render(Object model) {
- return gson.toJson(model);
+ try {
+ return objectMapper.writeValueAsString(model);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
}
public T fromJson(String json, Class type) {
- return gson.fromJson(json, type);
+ try {
+ return objectMapper.readValue(json, type);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
}
}
\ No newline at end of file
diff --git a/src/test/java/rocks/bastion/support/embedded/TestWithProxiedEmbeddedServer.java b/src/test/java/rocks/bastion/support/embedded/TestWithProxiedEmbeddedServer.java
index eadc3ec..2ca2f4e 100644
--- a/src/test/java/rocks/bastion/support/embedded/TestWithProxiedEmbeddedServer.java
+++ b/src/test/java/rocks/bastion/support/embedded/TestWithProxiedEmbeddedServer.java
@@ -1,8 +1,5 @@
package rocks.bastion.support.embedded;
-import com.mashape.unirest.http.Unirest;
-import com.mashape.unirest.http.options.Option;
-import com.mashape.unirest.http.options.Options;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.config.RegistryBuilder;
@@ -32,13 +29,11 @@ public static void setupProxying() {
DefaultSchemePortResolver schemePortResolver = prepareSchemePortResolver();
BasicHttpClientConnectionManager connManager = prepareConnectionManager(dnsResolver, schemePortResolver);
HttpClient httpClient = prepareHttpClient(connManager);
- originalHttpClient = (HttpClient) Options.getOption(Option.HTTPCLIENT);
- Unirest.setHttpClient(httpClient);
}
@AfterClass
public static void cleanupProxying() {
- Unirest.setHttpClient(originalHttpClient);
+ // TODO
}
private static DefaultSchemePortResolver prepareSchemePortResolver() {