Improve field type resolution for fields with optional ancestors
Fixes gh-567
This commit is contained in:
@@ -153,7 +153,8 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
|
||||
if (this.subsectionExtractor != null) {
|
||||
content = verifyContent(this.subsectionExtractor.extractSubsection(content, contentType));
|
||||
}
|
||||
ContentHandler contentHandler = ContentHandler.forContent(content, contentType);
|
||||
ContentHandler contentHandler = ContentHandler.forContentWithDescriptors(content, contentType,
|
||||
this.fieldDescriptors);
|
||||
|
||||
validateFieldDocumentation(contentHandler);
|
||||
|
||||
@@ -193,10 +194,9 @@ public abstract class AbstractFieldsSnippet extends TemplatedSnippet {
|
||||
}
|
||||
|
||||
private void validateFieldDocumentation(ContentHandler payloadHandler) {
|
||||
List<FieldDescriptor> missingFields = payloadHandler.findMissingFields(this.fieldDescriptors);
|
||||
List<FieldDescriptor> missingFields = payloadHandler.findMissingFields();
|
||||
|
||||
String undocumentedPayload = this.ignoreUndocumentedFields ? null
|
||||
: payloadHandler.getUndocumentedContent(this.fieldDescriptors);
|
||||
String undocumentedPayload = this.ignoreUndocumentedFields ? null : payloadHandler.getUndocumentedContent();
|
||||
|
||||
if (!missingFields.isEmpty() || StringUtils.hasText(undocumentedPayload)) {
|
||||
String message = "";
|
||||
|
||||
@@ -30,41 +30,40 @@ interface ContentHandler extends FieldTypeResolver {
|
||||
|
||||
/**
|
||||
* Finds the fields that are missing from the handler's payload. A field is missing if
|
||||
* it is described by one of the {@code fieldDescriptors} but is not present in the
|
||||
* payload.
|
||||
* @param fieldDescriptors the descriptors
|
||||
* it is described but is not present in the payload.
|
||||
* @return descriptors for the fields that are missing from the payload
|
||||
* @throws PayloadHandlingException if a failure occurs
|
||||
*/
|
||||
List<FieldDescriptor> findMissingFields(List<FieldDescriptor> fieldDescriptors);
|
||||
List<FieldDescriptor> findMissingFields();
|
||||
|
||||
/**
|
||||
* Returns modified content, formatted as a String, that only contains the fields that
|
||||
* are undocumented. A field is undocumented if it is present in the handler's content
|
||||
* but is not described by the given {@code fieldDescriptors}. If the content is
|
||||
* completely documented, {@code null} is returned
|
||||
* @param fieldDescriptors the descriptors
|
||||
* but is not described. If the content is completely documented, {@code null} is
|
||||
* returned
|
||||
* @return the undocumented content, or {@code null} if all of the content is
|
||||
* documented
|
||||
* @throws PayloadHandlingException if a failure occurs
|
||||
*/
|
||||
String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors);
|
||||
String getUndocumentedContent();
|
||||
|
||||
/**
|
||||
* Create a {@link ContentHandler} for the given content type and payload.
|
||||
* Create a {@link ContentHandler} for the given content type and payload, described
|
||||
* by the given descriptors.
|
||||
* @param content the payload
|
||||
* @param contentType the content type
|
||||
* @param descriptors descriptors of the content
|
||||
* @return the ContentHandler
|
||||
* @throws PayloadHandlingException if no known ContentHandler can handle the content
|
||||
*/
|
||||
static ContentHandler forContent(byte[] content, MediaType contentType) {
|
||||
|
||||
static ContentHandler forContentWithDescriptors(byte[] content, MediaType contentType,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
try {
|
||||
return new JsonContentHandler(content);
|
||||
return new JsonContentHandler(content, descriptors);
|
||||
}
|
||||
catch (Exception je) {
|
||||
try {
|
||||
return new XmlContentHandler(content);
|
||||
return new XmlContentHandler(content, descriptors);
|
||||
}
|
||||
catch (Exception xe) {
|
||||
throw new PayloadHandlingException(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.restdocs.payload;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
@@ -33,9 +36,25 @@ public interface FieldTypeResolver {
|
||||
* @param content the payload that the {@code FieldTypeResolver} should handle
|
||||
* @param contentType the content type of the payload
|
||||
* @return the {@code FieldTypeResolver}
|
||||
* @deprecated since 2.0.4 in favor of
|
||||
* {@link #forContentWithDescriptors(byte[], MediaType, List)}
|
||||
*/
|
||||
@Deprecated
|
||||
static FieldTypeResolver forContent(byte[] content, MediaType contentType) {
|
||||
return ContentHandler.forContent(content, contentType);
|
||||
return forContentWithDescriptors(content, contentType, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code FieldTypeResolver} for the given {@code content} and
|
||||
* {@code contentType}, described by the given {@code descriptors}.
|
||||
* @param content the payload that the {@code FieldTypeResolver} should handle
|
||||
* @param contentType the content type of the payload
|
||||
* @param descriptors the descriptors of the content
|
||||
* @return the {@code FieldTypeResolver}
|
||||
*/
|
||||
static FieldTypeResolver forContentWithDescriptors(byte[] content, MediaType contentType,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
return ContentHandler.forContentWithDescriptors(content, contentType, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,18 +44,21 @@ class JsonContentHandler implements ContentHandler {
|
||||
|
||||
private final byte[] rawContent;
|
||||
|
||||
JsonContentHandler(byte[] content) {
|
||||
private final List<FieldDescriptor> fieldDescriptors;
|
||||
|
||||
JsonContentHandler(byte[] content, List<FieldDescriptor> fieldDescriptors) {
|
||||
this.rawContent = content;
|
||||
this.fieldDescriptors = fieldDescriptors;
|
||||
readContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FieldDescriptor> findMissingFields(List<FieldDescriptor> fieldDescriptors) {
|
||||
public List<FieldDescriptor> findMissingFields() {
|
||||
List<FieldDescriptor> missingFields = new ArrayList<>();
|
||||
Object payload = readContent();
|
||||
for (FieldDescriptor fieldDescriptor : fieldDescriptors) {
|
||||
for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) {
|
||||
if (!fieldDescriptor.isOptional() && !this.fieldProcessor.hasField(fieldDescriptor.getPath(), payload)
|
||||
&& !isNestedBeneathMissingOptionalField(fieldDescriptor, fieldDescriptors, payload)) {
|
||||
&& !isNestedBeneathMissingOptionalField(fieldDescriptor, payload)) {
|
||||
missingFields.add(fieldDescriptor);
|
||||
}
|
||||
}
|
||||
@@ -63,9 +66,8 @@ class JsonContentHandler implements ContentHandler {
|
||||
return missingFields;
|
||||
}
|
||||
|
||||
private boolean isNestedBeneathMissingOptionalField(FieldDescriptor missing, List<FieldDescriptor> fieldDescriptors,
|
||||
Object payload) {
|
||||
List<FieldDescriptor> candidates = new ArrayList<>(fieldDescriptors);
|
||||
private boolean isNestedBeneathMissingOptionalField(FieldDescriptor missing, Object payload) {
|
||||
List<FieldDescriptor> candidates = new ArrayList<>(this.fieldDescriptors);
|
||||
candidates.remove(missing);
|
||||
for (FieldDescriptor candidate : candidates) {
|
||||
if (candidate.isOptional() && missing.getPath().startsWith(candidate.getPath())
|
||||
@@ -98,9 +100,9 @@ class JsonContentHandler implements ContentHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors) {
|
||||
public String getUndocumentedContent() {
|
||||
Object content = readContent();
|
||||
for (FieldDescriptor fieldDescriptor : fieldDescriptors) {
|
||||
for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) {
|
||||
if (describesSubsection(fieldDescriptor)) {
|
||||
this.fieldProcessor.removeSubsection(fieldDescriptor.getPath(), content);
|
||||
}
|
||||
@@ -154,7 +156,9 @@ class JsonContentHandler implements ContentHandler {
|
||||
.discoverFieldTypes(fieldDescriptor.getPath(), readContent())
|
||||
.coalesce(fieldDescriptor.isOptional());
|
||||
if (descriptorFieldType == JsonFieldType.VARIES || descriptorFieldType == actualFieldType
|
||||
|| (fieldDescriptor.isOptional() && actualFieldType == JsonFieldType.NULL)) {
|
||||
|| (fieldDescriptor.isOptional() && actualFieldType == JsonFieldType.NULL)
|
||||
|| (isNestedBeneathMissingOptionalField(fieldDescriptor, readContent())
|
||||
&& actualFieldType == JsonFieldType.VARIES)) {
|
||||
return descriptorFieldType;
|
||||
}
|
||||
throw new FieldTypesDoNotMatchException(fieldDescriptor, actualFieldType);
|
||||
|
||||
@@ -53,7 +53,9 @@ class XmlContentHandler implements ContentHandler {
|
||||
|
||||
private final byte[] rawContent;
|
||||
|
||||
XmlContentHandler(byte[] rawContent) {
|
||||
private final List<FieldDescriptor> fieldDescriptors;
|
||||
|
||||
XmlContentHandler(byte[] rawContent, List<FieldDescriptor> fieldDescriptors) {
|
||||
try {
|
||||
this.documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
}
|
||||
@@ -61,14 +63,15 @@ class XmlContentHandler implements ContentHandler {
|
||||
throw new IllegalStateException("Failed to create document builder", ex);
|
||||
}
|
||||
this.rawContent = rawContent;
|
||||
this.fieldDescriptors = fieldDescriptors;
|
||||
readPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FieldDescriptor> findMissingFields(List<FieldDescriptor> fieldDescriptors) {
|
||||
public List<FieldDescriptor> findMissingFields() {
|
||||
List<FieldDescriptor> missingFields = new ArrayList<>();
|
||||
Document payload = readPayload();
|
||||
for (FieldDescriptor fieldDescriptor : fieldDescriptors) {
|
||||
for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) {
|
||||
if (!fieldDescriptor.isOptional()) {
|
||||
NodeList matchingNodes = findMatchingNodes(fieldDescriptor, payload);
|
||||
if (matchingNodes.getLength() == 0) {
|
||||
@@ -103,10 +106,10 @@ class XmlContentHandler implements ContentHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUndocumentedContent(List<FieldDescriptor> fieldDescriptors) {
|
||||
public String getUndocumentedContent() {
|
||||
Document payload = readPayload();
|
||||
List<Node> matchedButNotRemoved = new ArrayList<>();
|
||||
for (FieldDescriptor fieldDescriptor : fieldDescriptors) {
|
||||
for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) {
|
||||
NodeList matchingNodes;
|
||||
try {
|
||||
matchingNodes = (NodeList) createXPath(fieldDescriptor.getPath()).evaluate(payload,
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.restdocs.payload;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
@@ -35,21 +37,43 @@ public class FieldTypeResolverTests {
|
||||
public ExpectedException thrownException = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void returnJsonFieldTypeResolver() {
|
||||
@Deprecated
|
||||
public void whenForContentCalledWithJsonContentThenReturnsJsonFieldTypeResolver() {
|
||||
assertThat(FieldTypeResolver.forContent("{\"field\": \"value\"}".getBytes(), MediaType.APPLICATION_JSON))
|
||||
.isInstanceOf(JsonContentHandler.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnXmlContentHandler() {
|
||||
@Deprecated
|
||||
public void whenForContentCalledWithXmlContentThenReturnsXmlContentHandler() {
|
||||
assertThat(FieldTypeResolver.forContent("<a><b>5</b></a>".getBytes(), MediaType.APPLICATION_XML))
|
||||
.isInstanceOf(XmlContentHandler.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwOnInvalidContent() {
|
||||
@Deprecated
|
||||
public void whenForContentIsCalledWithInvalidContentThenExceptionIsThrown() {
|
||||
this.thrownException.expect(PayloadHandlingException.class);
|
||||
FieldTypeResolver.forContent("some".getBytes(), MediaType.APPLICATION_XML);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenForContentWithDescriptorsCalledWithJsonContentThenReturnsJsonFieldTypeResolver() {
|
||||
assertThat(FieldTypeResolver.forContentWithDescriptors("{\"field\": \"value\"}".getBytes(),
|
||||
MediaType.APPLICATION_JSON, Collections.emptyList())).isInstanceOf(JsonContentHandler.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenForContentWithDescriptorsCalledWithXmlContentThenReturnsXmlContentHandler() {
|
||||
assertThat(FieldTypeResolver.forContentWithDescriptors("<a><b>5</b></a>".getBytes(), MediaType.APPLICATION_XML,
|
||||
Collections.emptyList())).isInstanceOf(XmlContentHandler.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenForContentWithDescriptorsIsCalledWithInvalidContentThenExceptionIsThrown() {
|
||||
this.thrownException.expect(PayloadHandlingException.class);
|
||||
FieldTypeResolver.forContentWithDescriptors("some".getBytes(), MediaType.APPLICATION_XML,
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.restdocs.payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Rule;
|
||||
@@ -39,137 +41,163 @@ public class JsonContentHandlerTests {
|
||||
@Test
|
||||
public void typeForFieldWithNullValueMustMatch() {
|
||||
this.thrown.expect(FieldTypesDoNotMatchException.class);
|
||||
new JsonContentHandler("{\"a\": null}".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a").type(JsonFieldType.STRING));
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a").type(JsonFieldType.STRING);
|
||||
new JsonContentHandler("{\"a\": null}".getBytes(), Arrays.asList(descriptor)).resolveFieldType(descriptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForFieldWithNotNullAndThenNullValueMustMatch() {
|
||||
this.thrown.expect(FieldTypesDoNotMatchException.class);
|
||||
new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a[].id").type(JsonFieldType.STRING));
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a[].id").type(JsonFieldType.STRING);
|
||||
new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}".getBytes(), Arrays.asList(descriptor))
|
||||
.resolveFieldType(descriptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForFieldWithNullAndThenNotNullValueMustMatch() {
|
||||
this.thrown.expect(FieldTypesDoNotMatchException.class);
|
||||
new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a.[].id").type(JsonFieldType.STRING));
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a.[].id").type(JsonFieldType.STRING);
|
||||
new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(), Arrays.asList(descriptor))
|
||||
.resolveFieldType(descriptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() {
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a[].id").optional());
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a[].id").optional();
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes(),
|
||||
Arrays.asList(descriptor)).resolveFieldType(descriptor);
|
||||
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForOptionalFieldWithNullAndThenNumberIsNumber() {
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a[].id").optional());
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a[].id").optional();
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(),
|
||||
Arrays.asList(descriptor)).resolveFieldType(descriptor);
|
||||
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForFieldWithNumberAndThenNullValueIsVaries() {
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a[].id"));
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a[].id");
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes(),
|
||||
Arrays.asList(descriptor)).resolveFieldType(descriptor);
|
||||
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForFieldWithNullAndThenNumberIsVaries() {
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a[].id"));
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a[].id");
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(),
|
||||
Arrays.asList(descriptor)).resolveFieldType(descriptor);
|
||||
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() {
|
||||
Object fieldType = new JsonContentHandler("{\"a\": null}".getBytes())
|
||||
.resolveFieldType(new FieldDescriptor("a").type(JsonFieldType.STRING).optional());
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a").type(JsonFieldType.STRING).optional();
|
||||
Object fieldType = new JsonContentHandler("{\"a\": null}".getBytes(), Arrays.asList(descriptor))
|
||||
.resolveFieldType(descriptor);
|
||||
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeForFieldWithSometimesPresentOptionalAncestorCanBeProvidedExplicitly() throws IOException {
|
||||
FieldDescriptor descriptor = new FieldDescriptor("a.[].b.c").type(JsonFieldType.NUMBER);
|
||||
FieldDescriptor ancestor = new FieldDescriptor("a.[].b").optional();
|
||||
Object fieldType = new JsonContentHandler("{\"a\":[ { \"d\": 4}, {\"b\":{\"c\":5}, \"d\": 4}]}".getBytes(),
|
||||
Arrays.asList(descriptor, ancestor)).resolveFieldType(descriptor);
|
||||
assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsFastWithNonJsonContent() {
|
||||
this.thrown.expect(PayloadHandlingException.class);
|
||||
new JsonContentHandler("Non-JSON content".getBytes());
|
||||
new JsonContentHandler("Non-JSON content".getBytes(), Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedFieldThatIsNotPresentIsConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes())
|
||||
.findMissingFields(
|
||||
Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"), new FieldDescriptor("c")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"),
|
||||
new FieldDescriptor("c"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes(),
|
||||
descriptors).findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(1);
|
||||
assertThat(missingFields.get(0).getPath()).isEqualTo("c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedOptionalFieldThatIsNotPresentIsNotConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"),
|
||||
new FieldDescriptor("c").optional()));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"),
|
||||
new FieldDescriptor("c").optional());
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes(),
|
||||
descriptors).findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsPresentIsConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":\"alpha\",\"b\":\"bravo\"}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"),
|
||||
new FieldDescriptor("a.c")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"),
|
||||
new FieldDescriptor("a.c"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":\"alpha\",\"b\":\"bravo\"}".getBytes(),
|
||||
descriptors).findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(1);
|
||||
assertThat(missingFields.get(0).getPath()).isEqualTo("a.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsNotPresentIsNotConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"b\":\"bravo\"}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"),
|
||||
new FieldDescriptor("a.c")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"),
|
||||
new FieldDescriptor("a.c"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"b\":\"bravo\"}".getBytes(), descriptors)
|
||||
.findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedFieldThatIsNotPresentNestedBeneathOptionalArrayThatIsEmptyIsNotConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"outer\":[]}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("outer"),
|
||||
new FieldDescriptor("outer[]").optional(), new FieldDescriptor("outer[].inner")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("outer"),
|
||||
new FieldDescriptor("outer[]").optional(), new FieldDescriptor("outer[].inner"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"outer\":[]}".getBytes(), descriptors)
|
||||
.findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedSometimesPresentFieldThatIsChildOfSometimesPresentOptionalArrayIsNotConsideredMissing() {
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a.[].c").optional(),
|
||||
new FieldDescriptor("a.[].c.d"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler(
|
||||
"{\"a\":[ {\"b\": \"bravo\"}, {\"b\": \"bravo\", \"c\": { \"d\": \"delta\"}}]}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("a.[].c").optional(),
|
||||
new FieldDescriptor("a.[].c.d")));
|
||||
"{\"a\":[ {\"b\": \"bravo\"}, {\"b\": \"bravo\", \"c\": { \"d\": \"delta\"}}]}".getBytes(), descriptors)
|
||||
.findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedMissingFieldThatIsChildOfNestedOptionalArrayThatIsEmptyIsNotConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":[{\"b\":[]}]}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("a.[].b").optional(),
|
||||
new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a.[].b").optional(),
|
||||
new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":[{\"b\":[]}]}".getBytes(), descriptors)
|
||||
.findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedMissingFieldThatIsChildOfNestedOptionalArrayThatContainsAnObjectIsConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":[{\"b\":[{}]}]}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("a.[].b").optional(),
|
||||
new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a.[].b").optional(),
|
||||
new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":[{\"b\":[{}]}]}".getBytes(), descriptors)
|
||||
.findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(1);
|
||||
assertThat(missingFields.get(0).getPath()).isEqualTo("a.[].b.[].c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void describedMissingFieldThatIsChildOfOptionalObjectThatIsNullIsNotConsideredMissing() {
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":null}".getBytes())
|
||||
.findMissingFields(Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("a.b")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(new FieldDescriptor("a").optional(),
|
||||
new FieldDescriptor("a.b"));
|
||||
List<FieldDescriptor> missingFields = new JsonContentHandler("{\"a\":null}".getBytes(), descriptors)
|
||||
.findMissingFields();
|
||||
assertThat(missingFields.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.restdocs.payload;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -38,56 +40,56 @@ public class XmlContentHandlerTests {
|
||||
|
||||
@Test
|
||||
public void topLevelElementCanBeDocumented() {
|
||||
String undocumentedContent = createHandler("<a>5</a>")
|
||||
.getUndocumentedContent(Arrays.asList(fieldWithPath("a").type("a").description("description")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(fieldWithPath("a").type("a").description("description"));
|
||||
String undocumentedContent = createHandler("<a>5</a>", descriptors).getUndocumentedContent();
|
||||
assertThat(undocumentedContent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedElementCanBeDocumentedLeavingAncestors() {
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>")
|
||||
.getUndocumentedContent(Arrays.asList(fieldWithPath("a/b").type("b").description("description")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(fieldWithPath("a/b").type("b").description("description"));
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>", descriptors).getUndocumentedContent();
|
||||
assertThat(undocumentedContent).isEqualTo(String.format("<a/>%n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldDescriptorDoesNotDocumentEntireSubsection() {
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>")
|
||||
.getUndocumentedContent(Arrays.asList(fieldWithPath("a").type("a").description("description")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(fieldWithPath("a").type("a").description("description"));
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>", descriptors).getUndocumentedContent();
|
||||
assertThat(undocumentedContent).isEqualTo(String.format("<a>%n <b>5</b>%n</a>%n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsectionDescriptorDocumentsEntireSubsection() {
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>")
|
||||
.getUndocumentedContent(Arrays.asList(subsectionWithPath("a").type("a").description("description")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(subsectionWithPath("a").type("a").description("description"));
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>", descriptors).getUndocumentedContent();
|
||||
assertThat(undocumentedContent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleElementsCanBeInDescendingOrderDocumented() {
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>")
|
||||
.getUndocumentedContent(Arrays.asList(fieldWithPath("a").type("a").description("description"),
|
||||
fieldWithPath("a/b").type("b").description("description")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(fieldWithPath("a").type("a").description("description"),
|
||||
fieldWithPath("a/b").type("b").description("description"));
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>", descriptors).getUndocumentedContent();
|
||||
assertThat(undocumentedContent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleElementsCanBeInAscendingOrderDocumented() {
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>")
|
||||
.getUndocumentedContent(Arrays.asList(fieldWithPath("a/b").type("b").description("description"),
|
||||
fieldWithPath("a").type("a").description("description")));
|
||||
List<FieldDescriptor> descriptors = Arrays.asList(fieldWithPath("a/b").type("b").description("description"),
|
||||
fieldWithPath("a").type("a").description("description"));
|
||||
String undocumentedContent = createHandler("<a><b>5</b></a>", descriptors).getUndocumentedContent();
|
||||
assertThat(undocumentedContent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsFastWithNonXmlContent() {
|
||||
this.thrown.expect(PayloadHandlingException.class);
|
||||
createHandler("non-XML content");
|
||||
createHandler("non-XML content", Collections.emptyList());
|
||||
}
|
||||
|
||||
private XmlContentHandler createHandler(String xml) {
|
||||
return new XmlContentHandler(xml.getBytes());
|
||||
private XmlContentHandler createHandler(String xml, List<FieldDescriptor> descriptors) {
|
||||
return new XmlContentHandler(xml.getBytes(), descriptors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user