diff --git a/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java b/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java index d3b0327..530c389 100644 --- a/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java +++ b/src/main/java/rocks/bastion/core/json/JsonSchemaAssertions.java @@ -8,9 +8,11 @@ import com.github.fge.jsonschema.core.report.ProcessingMessage; 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; import rocks.bastion.core.resource.ResourceLoader; import java.io.IOException; @@ -33,10 +35,12 @@ public static JsonSchemaAssertions fromResource(String expectedSchemaSource) { } private String expectedSchema; + private ContentType contentType; private JsonSchemaAssertions(String expectedSchema) { Objects.requireNonNull(expectedSchema); this.expectedSchema = expectedSchema; + contentType = ContentType.APPLICATION_JSON; } @Override @@ -44,6 +48,7 @@ public void execute(int statusCode, ModelResponse response, Object model) throws AssertionError { try { + assertContentTypeHeader(response); JsonNode jsonNodeOfResponse = convertResponseToJsonNode(response); assertResponseConformsToSchema(jsonNodeOfResponse); } catch (IOException e) { @@ -54,6 +59,20 @@ 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 + * this, this assertions object will still try to interpret the body as if it were JSON text. + * + * @param contentType The expected content-type header + * @return This object (for method chaining) + */ + public JsonSchemaAssertions overrideContentType(ContentType contentType) { + Objects.requireNonNull(contentType); + this.contentType = contentType; + return this; + } private JsonNode convertResponseToJsonNode(ModelResponse response) throws IOException { ObjectMapper mapper = new ObjectMapper(); @@ -77,4 +96,9 @@ private JsonNode getExpectedSchema() throws IOException { return mapper.readTree(expectedSchema); } + 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()); + } + } diff --git a/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java b/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java index 8260644..d100cd8 100644 --- a/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java +++ b/src/test/java/rocks/bastion/core/assertions/JsonSchemaAssertionsTest.java @@ -1,5 +1,6 @@ package rocks.bastion.core.assertions; +import org.apache.http.entity.ContentType; import org.junit.Assert; import org.junit.Test; import rocks.bastion.core.ModelResponse; @@ -35,6 +36,33 @@ public void execute_fromStringSchemaMismatch_assertionErrorShouldBeThrown() { Assert.fail("An assertion error should have been thrown by the JSON Schema Assertions"); } + @Test + public void execute_fromStringContentTypeMismatch_assertionErrorShouldBeThrown() { + try { + final JsonSchemaAssertions assertions = JsonSchemaAssertions.fromString("{\n" + + " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" + + " \"type\": \"object\",\n" + + " \"properties\": {\n" + + " \"id\": {\n" + + " \"type\": \"integer\"\n" + + " }\n" + + " },\n" + + " \"required\": [\n" + + " \"id\"\n" + + " ]\n" + + "}\n"); + ModelResponse response = TestModelResponse.prepare("{ \"id\": 21 }", "text/plain"); + 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>", + assertionError.getMessage()); + return; + } + + Assert.fail("An assertion error should have been thrown by the JSON Schema Assertions"); + } + @Test public void execute_fromStringSchemaMatches_shouldAssertSuccessfully() { final JsonSchemaAssertions assertions = JsonSchemaAssertions.fromString("{\n" + @@ -53,6 +81,25 @@ public void execute_fromStringSchemaMatches_shouldAssertSuccessfully() { assertions.execute(201, response, response.getModel()); } + @Test + public void execute_fromStringSchemaAndContentTypeMatches_shouldAssertSuccessfully() { + final JsonSchemaAssertions assertions = JsonSchemaAssertions.fromString("{\n" + + " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" + + " \"type\": \"object\",\n" + + " \"properties\": {\n" + + " \"id\": {\n" + + " \"type\": \"integer\"\n" + + " }\n" + + " },\n" + + " \"required\": [\n" + + " \"id\"\n" + + " ]\n" + + "}\n"); + assertions.overrideContentType(ContentType.TEXT_PLAIN); + ModelResponse response = TestModelResponse.prepare("{ \"id\": 22 }", "text/plain"); + assertions.execute(201, response, response.getModel()); + } + @Test(expected = InvalidJsonException.class) public void execute_fromStringSchemaIsInvalid_shouldThrowException() { final JsonSchemaAssertions assertions = JsonSchemaAssertions.fromString("{\n" + diff --git a/src/test/java/rocks/bastion/core/assertions/TestModelResponse.java b/src/test/java/rocks/bastion/core/assertions/TestModelResponse.java index 11162f3..8c00599 100644 --- a/src/test/java/rocks/bastion/core/assertions/TestModelResponse.java +++ b/src/test/java/rocks/bastion/core/assertions/TestModelResponse.java @@ -11,13 +11,28 @@ class TestModelResponse { /** * Prepare a model response object with a status code of HTTP 200 and the given JSON body for testing purposes. + * The content type of the response will be set to "application/json". * * @param jsonContent * @return A model response object. */ static ModelResponse prepare(String jsonContent) { - return new ModelResponse<>(new RawResponse(200, "OK", Collections.singletonList(new ApiHeader("Content-type", "application/json")), new ByteArrayInputStream(jsonContent.getBytes())), - jsonContent); + return prepare(jsonContent, "application/json"); + } + + /** + * Prepare a model response object with a status code of HTTP 200, as well as the given JSON body and content type, + * for testing purposes. + * + * @param jsonContent + * @param contentType + * @return A model response object. + */ + static ModelResponse prepare(String jsonContent, String contentType) { + return new ModelResponse<>(new RawResponse(200, + "OK", + Collections.singletonList(new ApiHeader("Content-type", contentType)), new ByteArrayInputStream(jsonContent.getBytes())), + jsonContent); } }