Add support for documenting the parts of a multipart request
Closes gh-161
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.request;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateFormats;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
|
||||
|
||||
/**
|
||||
* Tests for failures when rendering {@link RequestPartsSnippet} due to missing or
|
||||
* undocumented request parts.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestPartsSnippetFailureTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedSnippet snippet = new ExpectedSnippet(TemplateFormats.asciidoctor());
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void undocumentedPart() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo(
|
||||
"Request parts with the following names were" + " not documented: [a]"));
|
||||
new RequestPartsSnippet(Collections.<RequestPartDescriptor>emptyList())
|
||||
.document(new OperationBuilder("undocumented-part",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost")
|
||||
.part("a", "alpha".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingPart() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Request parts with the following names were"
|
||||
+ " not found in the request: [a]"));
|
||||
new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one")))
|
||||
.document(new OperationBuilder("missing-part",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedAndMissingParts() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Request parts with the following names were"
|
||||
+ " not documented: [b]. Request parts with the following"
|
||||
+ " names were not found in the request: [a]"));
|
||||
new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one")))
|
||||
.document(new OperationBuilder("undocumented-and-missing-parts",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost")
|
||||
.part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.restdocs.request;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.restdocs.AbstractSnippetTests;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateFormat;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
|
||||
/**
|
||||
* Tests for {@link RequestPartsSnippet}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestPartsSnippetTests extends AbstractSnippetTests {
|
||||
|
||||
public RequestPartsSnippetTests(String name, TemplateFormat templateFormat) {
|
||||
super(name, templateFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestParts() throws IOException {
|
||||
this.snippet.expectRequestParts("request-parts")
|
||||
.withContents(tableWithHeader("Part", "Description").row("`a`", "one")
|
||||
.row("`b`", "two"));
|
||||
new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one"),
|
||||
partWithName("b").description("two")))
|
||||
.document(operationBuilder("request-parts")
|
||||
.request("http://localhost").part("a", "bravo".getBytes())
|
||||
.and().part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoredRequestPart() throws IOException {
|
||||
this.snippet.expectRequestParts("ignored-request-part")
|
||||
.withContents(tableWithHeader("Part", "Description").row("`b`", "two"));
|
||||
new RequestPartsSnippet(Arrays.asList(partWithName("a").ignored(),
|
||||
partWithName("b").description("two")))
|
||||
.document(operationBuilder("ignored-request-part")
|
||||
.request("http://localhost").part("a", "bravo".getBytes())
|
||||
.and().part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allUndocumentedRequestPartsCanBeIgnored() throws IOException {
|
||||
this.snippet.expectRequestParts("ignore-all-undocumented")
|
||||
.withContents(tableWithHeader("Part", "Description").row("`b`", "two"));
|
||||
new RequestPartsSnippet(Arrays.asList(partWithName("b").description("two")), true)
|
||||
.document(operationBuilder("ignore-all-undocumented")
|
||||
.request("http://localhost").part("a", "bravo".getBytes()).and()
|
||||
.part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingOptionalRequestPart() throws IOException {
|
||||
this.snippet.expectRequestParts("missing-optional-request-parts")
|
||||
.withContents(tableWithHeader("Part", "Description").row("`a`", "one")
|
||||
.row("`b`", "two"));
|
||||
new RequestPartsSnippet(
|
||||
Arrays.asList(partWithName("a").description("one").optional(),
|
||||
partWithName("b").description("two"))).document(
|
||||
operationBuilder("missing-optional-request-parts")
|
||||
.request("http://localhost")
|
||||
.part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void presentOptionalRequestPart() throws IOException {
|
||||
this.snippet.expectRequestParts("present-optional-request-part")
|
||||
.withContents(tableWithHeader("Part", "Description").row("`a`", "one"));
|
||||
new RequestPartsSnippet(
|
||||
Arrays.asList(partWithName("a").description("one").optional()))
|
||||
.document(operationBuilder("present-optional-request-part")
|
||||
.request("http://localhost").part("a", "one".getBytes())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestPartsWithCustomAttributes() throws IOException {
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
given(resolver.resolveTemplateResource("request-parts"))
|
||||
.willReturn(snippetResource("request-parts-with-title"));
|
||||
this.snippet.expectRequestParts("request-parts-with-custom-attributes")
|
||||
.withContents(containsString("The title"));
|
||||
|
||||
new RequestPartsSnippet(
|
||||
Arrays.asList(
|
||||
partWithName("a").description("one")
|
||||
.attributes(key("foo").value("alpha")),
|
||||
partWithName("b").description("two")
|
||||
.attributes(key("foo").value("bravo"))),
|
||||
attributes(key("title").value("The title")))
|
||||
.document(operationBuilder("request-parts-with-custom-attributes")
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost").part("a", "alpha".getBytes())
|
||||
.and().part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestPartsWithCustomDescriptorAttributes() throws IOException {
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
given(resolver.resolveTemplateResource("request-parts"))
|
||||
.willReturn(snippetResource("request-parts-with-extra-column"));
|
||||
this.snippet.expectRequestParts("request-parts-with-custom-descriptor-attributes")
|
||||
.withContents(tableWithHeader("Part", "Description", "Foo")
|
||||
.row("a", "one", "alpha").row("b", "two", "bravo"));
|
||||
|
||||
new RequestPartsSnippet(Arrays.asList(
|
||||
partWithName("a").description("one")
|
||||
.attributes(key("foo").value("alpha")),
|
||||
partWithName("b").description("two")
|
||||
.attributes(key("foo").value("bravo"))))
|
||||
.document(operationBuilder(
|
||||
"request-parts-with-custom-descriptor-attributes")
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(
|
||||
resolver))
|
||||
.request("http://localhost")
|
||||
.part("a", "alpha".getBytes()).and()
|
||||
.part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestPartsWithOptionalColumn() throws IOException {
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
given(resolver.resolveTemplateResource("request-parts"))
|
||||
.willReturn(snippetResource("request-parts-with-optional-column"));
|
||||
this.snippet.expectRequestParts("request-parts-with-optional-column")
|
||||
.withContents(tableWithHeader("Part", "Optional", "Description")
|
||||
.row("a", "true", "one").row("b", "false", "two"));
|
||||
|
||||
new RequestPartsSnippet(
|
||||
Arrays.asList(partWithName("a").description("one").optional(),
|
||||
partWithName("b").description("two"))).document(
|
||||
operationBuilder("request-parts-with-optional-column")
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost")
|
||||
.part("a", "alpha".getBytes()).and()
|
||||
.part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalDescriptors() throws IOException {
|
||||
this.snippet.expectRequestParts("additional-descriptors")
|
||||
.withContents(tableWithHeader("Part", "Description").row("`a`", "one")
|
||||
.row("`b`", "two"));
|
||||
RequestDocumentation.requestParts(partWithName("a").description("one"))
|
||||
.and(partWithName("b").description("two"))
|
||||
.document(operationBuilder("additional-descriptors")
|
||||
.request("http://localhost").part("a", "bravo".getBytes()).and()
|
||||
.part("b", "bravo".getBytes()).build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -126,6 +126,11 @@ public class ExpectedSnippet implements TestRule {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectRequestParts(String name) {
|
||||
expect(name, "request-parts");
|
||||
return this;
|
||||
}
|
||||
|
||||
private ExpectedSnippet expect(String name, String type) {
|
||||
this.expectedName = name;
|
||||
this.expectedType = type;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Part|Description|Foo
|
||||
|
||||
{{#requestParts}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/requestParts}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Part|Optional|Description
|
||||
|
||||
{{#requestParts}}
|
||||
|{{name}}
|
||||
|{{optional}}
|
||||
|{{description}}
|
||||
|
||||
{{/requestParts}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Part|Description
|
||||
|
||||
{{#requestParts}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|
||||
{{/requestParts}}
|
||||
|===
|
||||
@@ -0,0 +1,5 @@
|
||||
Part | Description | Foo
|
||||
---- | ----------- | ---
|
||||
{{#requestParts}}
|
||||
{{name}} | {{description}} | {{foo}}
|
||||
{{/requestParts}}
|
||||
@@ -0,0 +1,5 @@
|
||||
Part | Optional | Description
|
||||
---- | -------- | -----------
|
||||
{{#requestParts}}
|
||||
{{name}} | {{optional}} | {{description}}
|
||||
{{/requestParts}}
|
||||
@@ -0,0 +1,6 @@
|
||||
{{title}}
|
||||
Part | Description
|
||||
---- | -----------
|
||||
{{#requestParts}}
|
||||
{{name}} | {{description}}
|
||||
{{/requestParts}}
|
||||
Reference in New Issue
Block a user