Isolate and reduce Spring Test dependencies
This commit splits Spring REST Docs into two projects – spring-restdocs-core and spring-restdocs-mockmvc. spring-restdocs-core contains the vast majority of the code but does not depend on a specific test framework other than JUnit. The use of a Spring Test TestExecutionListener has been replaced with a JUnit test rule. The rule is declared once per test class and configured with the output directory to which the generated snippets should be written. This simplifies the implementation as thread local storage is no longer required to transfer information about the test that’s running into Spring REST Docs. Instead, this transfer is now handled by the new test rule. It has also simplified the configuration as it’s no longer necessary for users to provide a system property that configures the output directory. spring-restdocs-mockmvc contains code that’s specific to using Spring REST Docs with Spring MVC Test’s MockMvc. This is currently the only testing framework that’s supported, but it paves the way for adding support for additional frameworks. REST Assured is one that users seem particularly interested in (see gh-80 and gh-102). Closes gh-107
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.constraints;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.validation.constraints.AssertFalse;
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.DecimalMax;
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.Digits;
|
||||
import javax.validation.constraints.Future;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Null;
|
||||
import javax.validation.constraints.Past;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConstraintDescriptions}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ConstraintDescriptionsTests {
|
||||
|
||||
private final ConstraintDescriptions constraintDescriptions = new ConstraintDescriptions(
|
||||
Constrained.class);
|
||||
|
||||
@Test
|
||||
public void assertFalse() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("assertFalse"),
|
||||
contains("Must be false"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertTrue() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("assertTrue"),
|
||||
contains("Must be true"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decimalMax() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("decimalMax"),
|
||||
contains("Must be at most 9.875"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decimalMin() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("decimalMin"),
|
||||
contains("Must be at least 1.5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void digits() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("digits"),
|
||||
contains("Must have at most 2 integral digits and 5 fractional digits"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void future() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("future"),
|
||||
contains("Must be in the future"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void max() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("max"),
|
||||
contains("Must be at most 10"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void min() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("min"),
|
||||
contains("Must be at least 5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notNull() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("notNull"),
|
||||
contains("Must not be null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nul() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("nul"),
|
||||
contains("Must be null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void past() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("past"),
|
||||
contains("Must be in the past"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pattern() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("pattern"),
|
||||
contains("Must match the regular expression '[A-Z][a-z]+'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void size() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("size"),
|
||||
contains("Size must be between 0 and 10 inclusive"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sizeList() {
|
||||
assertThat(
|
||||
this.constraintDescriptions.descriptionsForProperty("sizeList"),
|
||||
contains("Size must be between 1 and 4 inclusive",
|
||||
"Size must be between 8 and 10 inclusive"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unconstrained() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("unconstrained"),
|
||||
hasSize(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonExistentProperty() {
|
||||
assertThat(this.constraintDescriptions.descriptionsForProperty("doesNotExist"),
|
||||
hasSize(0));
|
||||
}
|
||||
|
||||
private static class Constrained {
|
||||
|
||||
@AssertFalse
|
||||
private boolean assertFalse;
|
||||
|
||||
@AssertTrue
|
||||
private boolean assertTrue;
|
||||
|
||||
@DecimalMax("9.875")
|
||||
private BigDecimal decimalMax;
|
||||
|
||||
@DecimalMin("1.5")
|
||||
private BigDecimal decimalMin;
|
||||
|
||||
@Digits(fraction = 5, integer = 2)
|
||||
private BigDecimal digits;
|
||||
|
||||
@Future
|
||||
private Date future;
|
||||
|
||||
@NotNull
|
||||
private String notNull;
|
||||
|
||||
@Max(10)
|
||||
private int max;
|
||||
|
||||
@Min(5)
|
||||
private int min;
|
||||
|
||||
@Null
|
||||
private String nul;
|
||||
|
||||
@Past
|
||||
private Date past;
|
||||
|
||||
@Pattern(regexp = "[A-Z][a-z]+")
|
||||
private String pattern;
|
||||
|
||||
@Size(min = 0, max = 10)
|
||||
private String size;
|
||||
|
||||
@Size.List({ @Size(min = 1, max = 4), @Size(min = 8, max = 10) })
|
||||
private String sizeList;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String unconstrained;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.constraints;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListResourceBundle;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.validation.constraints.AssertFalse;
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.DecimalMax;
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.Digits;
|
||||
import javax.validation.constraints.Future;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Null;
|
||||
import javax.validation.constraints.Past;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceBundleConstraintDescriptionResolver}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ResourceBundleConstraintDescriptionResolverTests {
|
||||
|
||||
private final ResourceBundleConstraintDescriptionResolver resolver = new ResourceBundleConstraintDescriptionResolver();
|
||||
|
||||
@Test
|
||||
public void defaultMessageAssertFalse() {
|
||||
String description = this.resolver.resolveDescription(new Constraint(
|
||||
AssertFalse.class.getName(), Collections.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Must be false")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageAssertTrue() {
|
||||
String description = this.resolver.resolveDescription(new Constraint(
|
||||
AssertTrue.class.getName(), Collections.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Must be true")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageDecimalMax() {
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put("value", "9.875");
|
||||
String description = this.resolver.resolveDescription(new Constraint(
|
||||
DecimalMax.class.getName(), configuration));
|
||||
assertThat(description, is(equalTo("Must be at most 9.875")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageDecimalMin() {
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put("value", "1.5");
|
||||
String description = this.resolver.resolveDescription(new Constraint(
|
||||
DecimalMin.class.getName(), configuration));
|
||||
assertThat(description, is(equalTo("Must be at least 1.5")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageDigits() {
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put("integer", "2");
|
||||
configuration.put("fraction", "5");
|
||||
String description = this.resolver.resolveDescription(new Constraint(Digits.class
|
||||
.getName(), configuration));
|
||||
assertThat(description, is(equalTo("Must have at most 2 integral digits and 5 "
|
||||
+ "fractional digits")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageFuture() {
|
||||
String description = this.resolver.resolveDescription(new Constraint(Future.class
|
||||
.getName(), Collections.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Must be in the future")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageMax() {
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put("value", 10);
|
||||
String description = this.resolver.resolveDescription(new Constraint(Max.class
|
||||
.getName(), configuration));
|
||||
assertThat(description, is(equalTo("Must be at most 10")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageMin() {
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put("value", 10);
|
||||
String description = this.resolver.resolveDescription(new Constraint(Min.class
|
||||
.getName(), configuration));
|
||||
assertThat(description, is(equalTo("Must be at least 10")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageNotNull() {
|
||||
String description = this.resolver.resolveDescription(new Constraint(
|
||||
NotNull.class.getName(), Collections.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Must not be null")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageNull() {
|
||||
String description = this.resolver.resolveDescription(new Constraint(Null.class
|
||||
.getName(), Collections.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Must be null")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessagePast() {
|
||||
String description = this.resolver.resolveDescription(new Constraint(Past.class
|
||||
.getName(), Collections.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Must be in the past")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessagePattern() {
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put("regexp", "[A-Z][a-z]+");
|
||||
String description = this.resolver.resolveDescription(new Constraint(
|
||||
Pattern.class.getName(), configuration));
|
||||
assertThat(description,
|
||||
is(equalTo("Must match the regular expression '[A-Z][a-z]+'")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultMessageSize() {
|
||||
Map<String, Object> configuration = new HashMap<>();
|
||||
configuration.put("min", 2);
|
||||
configuration.put("max", 10);
|
||||
String description = this.resolver.resolveDescription(new Constraint(Size.class
|
||||
.getName(), configuration));
|
||||
assertThat(description, is(equalTo("Size must be between 2 and 10 inclusive")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customMessage() {
|
||||
Thread.currentThread().setContextClassLoader(new ClassLoader() {
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
if (name.startsWith("org/springframework/restdocs/constraints/ConstraintDescriptions")) {
|
||||
return super
|
||||
.getResource("org/springframework/restdocs/constraints/TestConstraintDescriptions.properties");
|
||||
}
|
||||
return super.getResource(name);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
try {
|
||||
String description = new ResourceBundleConstraintDescriptionResolver()
|
||||
.resolveDescription(new Constraint(NotNull.class.getName(),
|
||||
Collections.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Should not be null")));
|
||||
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customResourceBundle() {
|
||||
ResourceBundle bundle = new ListResourceBundle() {
|
||||
|
||||
@Override
|
||||
protected Object[][] getContents() {
|
||||
return new String[][] { { NotNull.class.getName() + ".description",
|
||||
"Not null" } };
|
||||
}
|
||||
|
||||
};
|
||||
String description = new ResourceBundleConstraintDescriptionResolver(bundle)
|
||||
.resolveDescription(new Constraint(NotNull.class.getName(), Collections
|
||||
.<String, Object> emptyMap()));
|
||||
assertThat(description, is(equalTo("Not null")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.constraints;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.validation.Payload;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Null;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hibernate.validator.constraints.CompositionType;
|
||||
import org.hibernate.validator.constraints.ConstraintComposition;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link ValidatorConstraintResolver}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ValidatorConstraintResolverTests {
|
||||
|
||||
private final ValidatorConstraintResolver resolver = new ValidatorConstraintResolver();
|
||||
|
||||
@Test
|
||||
public void singleFieldConstraint() {
|
||||
List<Constraint> constraints = this.resolver.resolveForProperty("single",
|
||||
ConstrainedFields.class);
|
||||
assertThat(constraints, hasSize(1));
|
||||
assertThat(constraints.get(0).getName(), is(NotNull.class.getName()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void multipleFieldConstraints() {
|
||||
List<Constraint> constraints = this.resolver.resolveForProperty("multiple",
|
||||
ConstrainedFields.class);
|
||||
assertThat(constraints, hasSize(2));
|
||||
assertThat(
|
||||
constraints,
|
||||
containsInAnyOrder(constraint(NotNull.class), constraint(Size.class)
|
||||
.config("min", 8).config("max", 16)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noFieldConstraints() {
|
||||
List<Constraint> constraints = this.resolver.resolveForProperty("none",
|
||||
ConstrainedFields.class);
|
||||
assertThat(constraints, hasSize(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compositeConstraint() {
|
||||
List<Constraint> constraints = this.resolver.resolveForProperty("composite",
|
||||
ConstrainedFields.class);
|
||||
assertThat(constraints, hasSize(1));
|
||||
}
|
||||
|
||||
private static class ConstrainedFields {
|
||||
|
||||
@NotNull
|
||||
private String single;
|
||||
|
||||
@NotNull
|
||||
@Size(min = 8, max = 16)
|
||||
private String multiple;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String none;
|
||||
|
||||
@CompositeConstraint
|
||||
private String composite;
|
||||
}
|
||||
|
||||
@ConstraintComposition(CompositionType.OR)
|
||||
@Null
|
||||
@NotBlank
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@javax.validation.Constraint(validatedBy = {})
|
||||
public @interface CompositeConstraint {
|
||||
|
||||
String message() default "Must be null or not blank";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
||||
|
||||
private ConstraintMatcher constraint(final Class<? extends Annotation> annotation) {
|
||||
return new ConstraintMatcher(annotation);
|
||||
}
|
||||
|
||||
private static class ConstraintMatcher extends BaseMatcher<Constraint> {
|
||||
|
||||
private final Class<?> annotation;
|
||||
|
||||
private final Map<String, Object> configuration = new HashMap<>();
|
||||
|
||||
private ConstraintMatcher(Class<?> annotation) {
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
public ConstraintMatcher config(String key, Object value) {
|
||||
this.configuration.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
if (!(item instanceof Constraint)) {
|
||||
return false;
|
||||
}
|
||||
Constraint constraint = (Constraint) item;
|
||||
if (!constraint.getName().equals(this.annotation.getName())) {
|
||||
return false;
|
||||
}
|
||||
for (Entry<String, Object> entry : this.configuration.entrySet()) {
|
||||
if (!constraint.getConfiguration().get(entry.getKey())
|
||||
.equals(entry.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Constraint named " + this.annotation.getName()
|
||||
+ " with configuration " + this.configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.curl;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.codeBlock;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link CurlRequestSnippet}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Yann Le Guern
|
||||
* @author Dmitriy Mayboroda
|
||||
* @author Jonathan Pearlin
|
||||
*/
|
||||
public class CurlRequestSnippetTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void getRequest() throws IOException {
|
||||
this.snippet.expectCurlRequest("get-request").withContents(
|
||||
codeBlock("bash").content("$ curl 'http://localhost/foo' -i"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder("get-request",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonGetRequest() throws IOException {
|
||||
this.snippet.expectCurlRequest("non-get-request").withContents(
|
||||
codeBlock("bash").content("$ curl 'http://localhost/foo' -i -X POST"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder("non-get-request",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.method("POST").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithContent() throws IOException {
|
||||
this.snippet.expectCurlRequest("request-with-content").withContents(
|
||||
codeBlock("bash")
|
||||
.content("$ curl 'http://localhost/foo' -i -d 'content'"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder("request-with-content",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.content("content").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithQueryString() throws IOException {
|
||||
this.snippet.expectCurlRequest("request-with-query-string")
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo?param=value' -i"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"request-with-query-string", this.snippet.getOutputDirectory()).request(
|
||||
"http://localhost/foo?param=value").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithOneParameter() throws IOException {
|
||||
this.snippet.expectCurlRequest("post-request-with-one-parameter").withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'"));
|
||||
new CurlRequestSnippet()
|
||||
.document(new OperationBuilder("post-request-with-one-parameter",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo").method("POST").param("k1", "v1")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithMultipleParameters() throws IOException {
|
||||
this.snippet.expectCurlRequest("post-request-with-multiple-parameters")
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X POST"
|
||||
+ " -d 'k1=v1&k1=v1-bis&k2=v2'"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"post-request-with-multiple-parameters", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost/foo")
|
||||
.method("POST").param("k1", "v1", "v1-bis").param("k2", "v2").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithUrlEncodedParameter() throws IOException {
|
||||
this.snippet
|
||||
.expectCurlRequest("post-request-with-url-encoded-parameter")
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X POST -d 'k1=a%26b'"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"post-request-with-url-encoded-parameter", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost/foo")
|
||||
.method("POST").param("k1", "a&b").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putRequestWithOneParameter() throws IOException {
|
||||
this.snippet.expectCurlRequest("put-request-with-one-parameter").withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X PUT -d 'k1=v1'"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"put-request-with-one-parameter", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo").method("PUT").param("k1", "v1").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putRequestWithMultipleParameters() throws IOException {
|
||||
this.snippet.expectCurlRequest("put-request-with-multiple-parameters")
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X PUT"
|
||||
+ " -d 'k1=v1&k1=v1-bis&k2=v2'"));
|
||||
new CurlRequestSnippet()
|
||||
.document(new OperationBuilder("put-request-with-multiple-parameters",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo").method("PUT").param("k1", "v1")
|
||||
.param("k1", "v1-bis").param("k2", "v2").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putRequestWithUrlEncodedParameter() throws IOException {
|
||||
this.snippet.expectCurlRequest("put-request-with-url-encoded-parameter")
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X PUT -d 'k1=a%26b'"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"put-request-with-url-encoded-parameter", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost/foo")
|
||||
.method("PUT").param("k1", "a&b").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithHeaders() throws IOException {
|
||||
this.snippet.expectCurlRequest("request-with-headers").withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i"
|
||||
+ " -H 'Content-Type: application/json' -H 'a: alpha'"));
|
||||
new CurlRequestSnippet().document(new OperationBuilder("request-with-headers",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.header("a", "alpha").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPostWithNoSubmittedFileName() throws IOException {
|
||||
String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H "
|
||||
+ "'Content-Type: multipart/form-data' -F "
|
||||
+ "'metadata={\"description\": \"foo\"}'";
|
||||
this.snippet.expectCurlRequest("multipart-post-no-original-filename")
|
||||
.withContents(codeBlock("bash").content(expectedContent));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"multipart-post-no-original-filename", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/upload").method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.part("metadata", "{\"description\": \"foo\"}".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPostWithContentType() throws IOException {
|
||||
String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H "
|
||||
+ "'Content-Type: multipart/form-data' -F "
|
||||
+ "'image=@documents/images/example.png;type=image/png'";
|
||||
this.snippet.expectCurlRequest("multipart-post-with-content-type").withContents(
|
||||
codeBlock("bash").content(expectedContent));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"multipart-post-with-content-type", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/upload").method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.part("image", new byte[0])
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE)
|
||||
.submittedFileName("documents/images/example.png").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPost() throws IOException {
|
||||
String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H "
|
||||
+ "'Content-Type: multipart/form-data' -F "
|
||||
+ "'image=@documents/images/example.png'";
|
||||
this.snippet.expectCurlRequest("multipart-post").withContents(
|
||||
codeBlock("bash").content(expectedContent));
|
||||
new CurlRequestSnippet().document(new OperationBuilder("multipart-post",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/upload")
|
||||
.method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.part("image", new byte[0])
|
||||
.submittedFileName("documents/images/example.png").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPostWithParameters() throws IOException {
|
||||
String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H "
|
||||
+ "'Content-Type: multipart/form-data' -F "
|
||||
+ "'image=@documents/images/example.png' -F 'a=apple' -F 'a=avocado' "
|
||||
+ "-F 'b=banana'";
|
||||
this.snippet.expectCurlRequest("multipart-post-with-parameters").withContents(
|
||||
codeBlock("bash").content(expectedContent));
|
||||
new CurlRequestSnippet().document(new OperationBuilder(
|
||||
"multipart-post-with-parameters", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/upload").method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.part("image", new byte[0])
|
||||
.submittedFileName("documents/images/example.png").and()
|
||||
.param("a", "apple", "avocado").param("b", "banana").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAttributes() throws IOException {
|
||||
this.snippet.expectCurlRequest("custom-attributes").withContents(
|
||||
containsString("curl request title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("curl-request"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/curl-request-with-title.snippet"));
|
||||
new CurlRequestSnippet(attributes(key("title").value("curl request title")))
|
||||
.document(new OperationBuilder("custom-attributes", this.snippet
|
||||
.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost/foo").build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.http;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.httpRequest;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link HttpRequestSnippet}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Jonathan Pearlin
|
||||
*
|
||||
*/
|
||||
public class HttpRequestSnippetTests {
|
||||
|
||||
private static final String BOUNDARY = "6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm";
|
||||
|
||||
@Rule
|
||||
public final ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Test
|
||||
public void getRequest() throws IOException {
|
||||
this.snippet.expectHttpRequest("get-request").withContents(
|
||||
httpRequest(GET, "/foo").header(HttpHeaders.HOST, "localhost").header(
|
||||
"Alpha", "a"));
|
||||
|
||||
new HttpRequestSnippet().document(new OperationBuilder("get-request",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.header("Alpha", "a").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestWithQueryString() throws IOException {
|
||||
this.snippet.expectHttpRequest("get-request-with-query-string").withContents(
|
||||
httpRequest(GET, "/foo?bar=baz").header(HttpHeaders.HOST, "localhost"));
|
||||
|
||||
new HttpRequestSnippet().document(new OperationBuilder(
|
||||
"get-request-with-query-string", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo?bar=baz").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithContent() throws IOException {
|
||||
this.snippet.expectHttpRequest("post-request-with-content").withContents(
|
||||
httpRequest(POST, "/foo").header(HttpHeaders.HOST, "localhost").content(
|
||||
"Hello, world"));
|
||||
|
||||
new HttpRequestSnippet().document(new OperationBuilder(
|
||||
"post-request-with-content", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo").method("POST").content("Hello, world")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithParameter() throws IOException {
|
||||
this.snippet.expectHttpRequest("post-request-with-parameter").withContents(
|
||||
httpRequest(POST, "/foo").header(HttpHeaders.HOST, "localhost")
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.content("b%26r=baz&a=alpha"));
|
||||
|
||||
new HttpRequestSnippet().document(new OperationBuilder(
|
||||
"post-request-with-parameter", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo").method("POST").param("b&r", "baz")
|
||||
.param("a", "alpha").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putRequestWithContent() throws IOException {
|
||||
this.snippet.expectHttpRequest("put-request-with-content").withContents(
|
||||
httpRequest(PUT, "/foo").header(HttpHeaders.HOST, "localhost").content(
|
||||
"Hello, world"));
|
||||
|
||||
new HttpRequestSnippet().document(new OperationBuilder(
|
||||
"put-request-with-content", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo").method("PUT").content("Hello, world")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putRequestWithParameter() throws IOException {
|
||||
this.snippet.expectHttpRequest("put-request-with-parameter").withContents(
|
||||
httpRequest(PUT, "/foo").header(HttpHeaders.HOST, "localhost")
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.content("b%26r=baz&a=alpha"));
|
||||
|
||||
new HttpRequestSnippet().document(new OperationBuilder(
|
||||
"put-request-with-parameter", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo").method("PUT").param("b&r", "baz")
|
||||
.param("a", "alpha").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPost() throws IOException {
|
||||
String expectedContent = createPart(String.format("Content-Disposition: "
|
||||
+ "form-data; " + "name=image%n%n<< data >>"));
|
||||
this.snippet.expectHttpRequest("multipart-post").withContents(
|
||||
httpRequest(POST, "/upload")
|
||||
.header(HttpHeaders.HOST, "localhost")
|
||||
.header("Content-Type",
|
||||
"multipart/form-data; boundary=" + BOUNDARY)
|
||||
.content(expectedContent));
|
||||
new HttpRequestSnippet().document(new OperationBuilder("multipart-post",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/upload")
|
||||
.method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.part("image", "<< data >>".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPostWithParameters() throws IOException {
|
||||
String param1Part = createPart(String.format("Content-Disposition: form-data; "
|
||||
+ "name=a%n%napple"), false);
|
||||
String param2Part = createPart(String.format("Content-Disposition: form-data; "
|
||||
+ "name=a%n%navocado"), false);
|
||||
String param3Part = createPart(String.format("Content-Disposition: form-data; "
|
||||
+ "name=b%n%nbanana"), false);
|
||||
String filePart = createPart(String.format("Content-Disposition: form-data; "
|
||||
+ "name=image%n%n<< data >>"));
|
||||
String expectedContent = param1Part + param2Part + param3Part + filePart;
|
||||
this.snippet.expectHttpRequest("multipart-post-with-parameters").withContents(
|
||||
httpRequest(POST, "/upload")
|
||||
.header(HttpHeaders.HOST, "localhost")
|
||||
.header("Content-Type",
|
||||
"multipart/form-data; boundary=" + BOUNDARY)
|
||||
.content(expectedContent));
|
||||
new HttpRequestSnippet().document(new OperationBuilder(
|
||||
"multipart-post-with-parameters", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/upload").method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.param("a", "apple", "avocado").param("b", "banana")
|
||||
.part("image", "<< data >>".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPostWithContentType() throws IOException {
|
||||
String expectedContent = createPart(String
|
||||
.format("Content-Disposition: form-data; name=image%nContent-Type: "
|
||||
+ "image/png%n%n<< data >>"));
|
||||
this.snippet.expectHttpRequest("multipart-post-with-content-type").withContents(
|
||||
httpRequest(POST, "/upload")
|
||||
.header(HttpHeaders.HOST, "localhost")
|
||||
.header("Content-Type",
|
||||
"multipart/form-data; boundary=" + BOUNDARY)
|
||||
.content(expectedContent));
|
||||
new HttpRequestSnippet().document(new OperationBuilder(
|
||||
"multipart-post-with-content-type", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/upload").method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.part("image", "<< data >>".getBytes())
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestWithCustomHost() throws IOException {
|
||||
this.snippet.expectHttpRequest("get-request-custom-host").withContents(
|
||||
httpRequest(GET, "/foo").header(HttpHeaders.HOST, "api.example.com"));
|
||||
new HttpRequestSnippet().document(new OperationBuilder("get-request-custom-host",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.header(HttpHeaders.HOST, "api.example.com").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithCustomSnippetAttributes() throws IOException {
|
||||
this.snippet.expectHttpRequest("request-with-snippet-attributes").withContents(
|
||||
containsString("Title for the request"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("http-request"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/http-request-with-title.snippet"));
|
||||
new HttpRequestSnippet(attributes(key("title").value("Title for the request")))
|
||||
.document(new OperationBuilder("request-with-snippet-attributes",
|
||||
this.snippet.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost/foo").build());
|
||||
}
|
||||
|
||||
private String createPart(String content) {
|
||||
return this.createPart(content, true);
|
||||
}
|
||||
|
||||
private String createPart(String content, boolean last) {
|
||||
StringBuilder part = new StringBuilder();
|
||||
part.append(String.format("--%s%n%s%n", BOUNDARY, content));
|
||||
if (last) {
|
||||
part.append(String.format("--%s--", BOUNDARY));
|
||||
}
|
||||
return part.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.http;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link HttpResponseSnippet}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Jonathan Pearlin
|
||||
*/
|
||||
public class HttpResponseSnippetTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Test
|
||||
public void basicResponse() throws IOException {
|
||||
this.snippet.expectHttpResponse("basic-response").withContents(httpResponse(OK));
|
||||
new HttpResponseSnippet().document(new OperationBuilder("basic-response",
|
||||
this.snippet.getOutputDirectory()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonOkResponse() throws IOException {
|
||||
this.snippet.expectHttpResponse("non-ok-response").withContents(
|
||||
httpResponse(BAD_REQUEST));
|
||||
new HttpResponseSnippet().document(new OperationBuilder("non-ok-response",
|
||||
this.snippet.getOutputDirectory()).response().status(BAD_REQUEST.value())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseWithHeaders() throws IOException {
|
||||
this.snippet.expectHttpResponse("response-with-headers").withContents(
|
||||
httpResponse(OK) //
|
||||
.header("Content-Type", "application/json") //
|
||||
.header("a", "alpha"));
|
||||
new HttpResponseSnippet().document(new OperationBuilder("response-with-headers",
|
||||
this.snippet.getOutputDirectory()).response()
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.header("a", "alpha").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseWithContent() throws IOException {
|
||||
this.snippet.expectHttpResponse("response-with-content").withContents(
|
||||
httpResponse(OK).content("content"));
|
||||
new HttpResponseSnippet().document(new OperationBuilder("response-with-content",
|
||||
this.snippet.getOutputDirectory()).response().content("content").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseWithCustomSnippetAttributes() throws IOException {
|
||||
this.snippet.expectHttpResponse("response-with-snippet-attributes").withContents(
|
||||
containsString("Title for the response"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("http-response"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/http-response-with-title.snippet"));
|
||||
new HttpResponseSnippet(attributes(key("title").value("Title for the response")))
|
||||
.document(new OperationBuilder("response-with-snippet-attributes",
|
||||
this.snippet.getOutputDirectory()).attribute(
|
||||
TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.hypermedia;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.operation.StandardOperationResponse;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContentTypeLinkExtractor}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ContentTypeLinkExtractorTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void extractionFailsWithNullContentType() throws IOException {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
new ContentTypeLinkExtractor().extractLinks(new StandardOperationResponse(
|
||||
HttpStatus.OK, new HttpHeaders(), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractorCalledWithMatchingContextType() throws IOException {
|
||||
Map<MediaType, LinkExtractor> extractors = new HashMap<>();
|
||||
LinkExtractor extractor = mock(LinkExtractor.class);
|
||||
extractors.put(MediaType.APPLICATION_JSON, extractor);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK,
|
||||
httpHeaders, null);
|
||||
new ContentTypeLinkExtractor(extractors).extractLinks(response);
|
||||
verify(extractor).extractLinks(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractorCalledWithCompatibleContextType() throws IOException {
|
||||
Map<MediaType, LinkExtractor> extractors = new HashMap<>();
|
||||
LinkExtractor extractor = mock(LinkExtractor.class);
|
||||
extractors.put(MediaType.APPLICATION_JSON, extractor);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.parseMediaType("application/json;foo=bar"));
|
||||
StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK,
|
||||
httpHeaders, null);
|
||||
new ContentTypeLinkExtractor(extractors).extractLinks(response);
|
||||
verify(extractor).extractLinks(response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.hypermedia;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.StandardOperationResponse;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Parameterized tests for {@link HalLinkExtractor} and {@link AtomLinkExtractor} with
|
||||
* various payloads.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class LinkExtractorsPayloadTests {
|
||||
|
||||
private final LinkExtractor linkExtractor;
|
||||
|
||||
private final String linkType;
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[] { new HalLinkExtractor(), "hal" },
|
||||
new Object[] { new AtomLinkExtractor(), "atom" });
|
||||
}
|
||||
|
||||
public LinkExtractorsPayloadTests(LinkExtractor linkExtractor, String linkType) {
|
||||
this.linkExtractor = linkExtractor;
|
||||
this.linkType = linkType;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleLink() throws IOException {
|
||||
Map<String, List<Link>> links = this.linkExtractor
|
||||
.extractLinks(createResponse("single-link"));
|
||||
assertLinks(Arrays.asList(new Link("alpha", "http://alpha.example.com")), links);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleLinksWithDifferentRels() throws IOException {
|
||||
Map<String, List<Link>> links = this.linkExtractor
|
||||
.extractLinks(createResponse("multiple-links-different-rels"));
|
||||
assertLinks(Arrays.asList(new Link("alpha", "http://alpha.example.com"),
|
||||
new Link("bravo", "http://bravo.example.com")), links);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleLinksWithSameRels() throws IOException {
|
||||
Map<String, List<Link>> links = this.linkExtractor
|
||||
.extractLinks(createResponse("multiple-links-same-rels"));
|
||||
assertLinks(Arrays.asList(new Link("alpha", "http://alpha.example.com/one"),
|
||||
new Link("alpha", "http://alpha.example.com/two")), links);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLinks() throws IOException {
|
||||
Map<String, List<Link>> links = this.linkExtractor
|
||||
.extractLinks(createResponse("no-links"));
|
||||
assertLinks(Collections.<Link> emptyList(), links);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linksInTheWrongFormat() throws IOException {
|
||||
Map<String, List<Link>> links = this.linkExtractor
|
||||
.extractLinks(createResponse("wrong-format"));
|
||||
assertLinks(Collections.<Link> emptyList(), links);
|
||||
}
|
||||
|
||||
private void assertLinks(List<Link> expectedLinks, Map<String, List<Link>> actualLinks) {
|
||||
MultiValueMap<String, Link> expectedLinksByRel = new LinkedMultiValueMap<>();
|
||||
for (Link expectedLink : expectedLinks) {
|
||||
expectedLinksByRel.add(expectedLink.getRel(), expectedLink);
|
||||
}
|
||||
assertEquals(expectedLinksByRel, actualLinks);
|
||||
}
|
||||
|
||||
private OperationResponse createResponse(String contentName) throws IOException {
|
||||
return new StandardOperationResponse(HttpStatus.OK, null,
|
||||
FileCopyUtils.copyToByteArray(getPayloadFile(contentName)));
|
||||
}
|
||||
|
||||
private File getPayloadFile(String name) {
|
||||
return new File("src/test/resources/link-payloads/" + this.linkType + "/" + name
|
||||
+ ".json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.hypermedia;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
|
||||
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.core.io.FileSystemResource;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Tests for {@link LinksSnippet}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class LinksSnippetTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void undocumentedLink() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Links with the following relations were not"
|
||||
+ " documented: [foo]"));
|
||||
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")),
|
||||
Collections.<LinkDescriptor> emptyList()).document(new OperationBuilder(
|
||||
"undocumented-link", this.snippet.getOutputDirectory()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingLink() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Links with the following relations were not"
|
||||
+ " found in the response: [foo]"));
|
||||
new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo")
|
||||
.description("bar"))).document(new OperationBuilder("missing-link",
|
||||
this.snippet.getOutputDirectory()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void documentedOptionalLink() throws IOException {
|
||||
this.snippet.expectLinks("documented-optional-link").withContents( //
|
||||
tableWithHeader("Relation", "Description") //
|
||||
.row("foo", "bar"));
|
||||
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "blah")),
|
||||
Arrays.asList(new LinkDescriptor("foo").description("bar").optional()))
|
||||
.document(new OperationBuilder("documented-optional-link", this.snippet
|
||||
.getOutputDirectory()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingOptionalLink() throws IOException {
|
||||
this.snippet.expectLinks("missing-optional-link").withContents( //
|
||||
tableWithHeader("Relation", "Description") //
|
||||
.row("foo", "bar"));
|
||||
new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo")
|
||||
.description("bar").optional())).document(new OperationBuilder(
|
||||
"missing-optional-link", this.snippet.getOutputDirectory()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedLinkAndMissingLink() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Links with the following relations were not"
|
||||
+ " documented: [a]. Links with the following relations were not"
|
||||
+ " found in the response: [foo]"));
|
||||
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")),
|
||||
Arrays.asList(new LinkDescriptor("foo").description("bar")))
|
||||
.document(new OperationBuilder("undocumented-link-and-missing-link",
|
||||
this.snippet.getOutputDirectory()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void documentedLinks() throws IOException {
|
||||
this.snippet.expectLinks("documented-links").withContents( //
|
||||
tableWithHeader("Relation", "Description") //
|
||||
.row("a", "one") //
|
||||
.row("b", "two"));
|
||||
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"),
|
||||
new Link("b", "bravo")), Arrays.asList(
|
||||
new LinkDescriptor("a").description("one"),
|
||||
new LinkDescriptor("b").description("two")))
|
||||
.document(new OperationBuilder("documented-links", this.snippet
|
||||
.getOutputDirectory()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linksWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectLinks("links-with-custom-descriptor-attributes").withContents( //
|
||||
tableWithHeader("Relation", "Description", "Foo") //
|
||||
.row("a", "one", "alpha") //
|
||||
.row("b", "two", "bravo"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("links"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/links-with-extra-column.snippet"));
|
||||
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"),
|
||||
new Link("b", "bravo")), Arrays.asList(
|
||||
new LinkDescriptor("a").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
new LinkDescriptor("b").description("two").attributes(
|
||||
key("foo").value("bravo")))).document(new OperationBuilder(
|
||||
"links-with-custom-descriptor-attributes", this.snippet
|
||||
.getOutputDirectory()).attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linksWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectLinks("links-with-custom-attributes").withContents(
|
||||
startsWith(".Title for the links"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("links"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/links-with-title.snippet"));
|
||||
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"),
|
||||
new Link("b", "bravo")), attributes(key("title").value(
|
||||
"Title for the links")), Arrays.asList(
|
||||
new LinkDescriptor("a").description("one"),
|
||||
new LinkDescriptor("b").description("two")))
|
||||
.document(new OperationBuilder("links-with-custom-attributes",
|
||||
this.snippet.getOutputDirectory()).attribute(
|
||||
TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).build());
|
||||
}
|
||||
|
||||
private static class StubLinkExtractor implements LinkExtractor {
|
||||
|
||||
private MultiValueMap<String, Link> linksByRel = new LinkedMultiValueMap<String, Link>();
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, Link> extractLinks(OperationResponse response)
|
||||
throws IOException {
|
||||
return this.linksByRel;
|
||||
}
|
||||
|
||||
private StubLinkExtractor withLinks(Link... links) {
|
||||
for (Link link : links) {
|
||||
this.linksByRel.add(link.getRel(), link);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.operation.preprocess;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
import org.springframework.restdocs.operation.OperationRequestPart;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.Parameters;
|
||||
import org.springframework.restdocs.operation.StandardOperationRequest;
|
||||
import org.springframework.restdocs.operation.StandardOperationResponse;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContentModifyingOperationPreprocessor}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class ContentModifyingOperationPreprocessorTests {
|
||||
|
||||
private final ContentModifyingOperationPreprocessor preprocessor = new ContentModifyingOperationPreprocessor(
|
||||
new ContentModifier() {
|
||||
|
||||
@Override
|
||||
public byte[] modifyContent(byte[] originalContent) {
|
||||
return "modified".getBytes();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Test
|
||||
public void modifyRequestContent() {
|
||||
StandardOperationRequest request = new StandardOperationRequest(
|
||||
URI.create("http://localhost"), HttpMethod.GET, "content".getBytes(),
|
||||
new HttpHeaders(), new Parameters(),
|
||||
Collections.<OperationRequestPart> emptyList());
|
||||
OperationRequest preprocessed = this.preprocessor.preprocess(request);
|
||||
assertThat(preprocessed.getContent(), is(equalTo("modified".getBytes())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modifyResponseContent() {
|
||||
StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK,
|
||||
new HttpHeaders(), "content".getBytes());
|
||||
OperationResponse preprocessed = this.preprocessor.preprocess(response);
|
||||
assertThat(preprocessed.getContent(), is(equalTo("modified".getBytes())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownContentLengthIsUnchanged() {
|
||||
StandardOperationRequest request = new StandardOperationRequest(
|
||||
URI.create("http://localhost"), HttpMethod.GET, "content".getBytes(),
|
||||
new HttpHeaders(), new Parameters(),
|
||||
Collections.<OperationRequestPart> emptyList());
|
||||
OperationRequest preprocessed = this.preprocessor.preprocess(request);
|
||||
assertThat(preprocessed.getHeaders().getContentLength(), is(equalTo(-1L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLengthIsUpdated() {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentLength(7);
|
||||
StandardOperationRequest request = new StandardOperationRequest(
|
||||
URI.create("http://localhost"), HttpMethod.GET, "content".getBytes(),
|
||||
httpHeaders, new Parameters(),
|
||||
Collections.<OperationRequestPart> emptyList());
|
||||
OperationRequest preprocessed = this.preprocessor.preprocess(request);
|
||||
assertThat(preprocessed.getHeaders().getContentLength(), is(equalTo(8L)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.operation.preprocess;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
|
||||
/**
|
||||
* Tests for {@link DelegatingOperationRequestPreprocessor}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class DelegatingOperationRequestPreprocessorTests {
|
||||
|
||||
@Test
|
||||
public void delegationOccurs() {
|
||||
OperationRequest originalRequest = mock(OperationRequest.class);
|
||||
|
||||
OperationPreprocessor preprocessor1 = mock(OperationPreprocessor.class);
|
||||
OperationRequest preprocessedRequest1 = mock(OperationRequest.class);
|
||||
when(preprocessor1.preprocess(originalRequest)).thenReturn(preprocessedRequest1);
|
||||
|
||||
OperationPreprocessor preprocessor2 = mock(OperationPreprocessor.class);
|
||||
OperationRequest preprocessedRequest2 = mock(OperationRequest.class);
|
||||
when(preprocessor2.preprocess(preprocessedRequest1)).thenReturn(
|
||||
preprocessedRequest2);
|
||||
|
||||
OperationPreprocessor preprocessor3 = mock(OperationPreprocessor.class);
|
||||
OperationRequest preprocessedRequest3 = mock(OperationRequest.class);
|
||||
when(preprocessor3.preprocess(preprocessedRequest2)).thenReturn(
|
||||
preprocessedRequest3);
|
||||
|
||||
OperationRequest result = new DelegatingOperationRequestPreprocessor(
|
||||
Arrays.asList(preprocessor1, preprocessor2, preprocessor3))
|
||||
.preprocess(originalRequest);
|
||||
|
||||
assertThat(result, is(preprocessedRequest3));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.operation.preprocess;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
|
||||
/**
|
||||
* Tests for {@link DelegatingOperationResponsePreprocessor}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class DelegatingOperationResponsePreprocessorTests {
|
||||
|
||||
@Test
|
||||
public void delegationOccurs() {
|
||||
OperationResponse originalResponse = mock(OperationResponse.class);
|
||||
|
||||
OperationPreprocessor preprocessor1 = mock(OperationPreprocessor.class);
|
||||
OperationResponse preprocessedResponse1 = mock(OperationResponse.class);
|
||||
when(preprocessor1.preprocess(originalResponse))
|
||||
.thenReturn(preprocessedResponse1);
|
||||
|
||||
OperationPreprocessor preprocessor2 = mock(OperationPreprocessor.class);
|
||||
OperationResponse preprocessedResponse2 = mock(OperationResponse.class);
|
||||
when(preprocessor2.preprocess(preprocessedResponse1)).thenReturn(
|
||||
preprocessedResponse2);
|
||||
|
||||
OperationPreprocessor preprocessor3 = mock(OperationPreprocessor.class);
|
||||
OperationResponse preprocessedResponse3 = mock(OperationResponse.class);
|
||||
when(preprocessor3.preprocess(preprocessedResponse2)).thenReturn(
|
||||
preprocessedResponse3);
|
||||
|
||||
OperationResponse result = new DelegatingOperationResponsePreprocessor(
|
||||
Arrays.asList(preprocessor1, preprocessor2, preprocessor3))
|
||||
.preprocess(originalResponse);
|
||||
|
||||
assertThat(result, is(preprocessedResponse3));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.operation.preprocess;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
import org.springframework.restdocs.operation.OperationRequestPart;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.Parameters;
|
||||
import org.springframework.restdocs.operation.StandardOperationRequest;
|
||||
import org.springframework.restdocs.operation.StandardOperationResponse;
|
||||
|
||||
/**
|
||||
* Tests for {@link HeaderRemovingOperationPreprocessorTests}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class HeaderRemovingOperationPreprocessorTests {
|
||||
|
||||
private final HeaderRemovingOperationPreprocessor preprocessor = new HeaderRemovingOperationPreprocessor(
|
||||
"b");
|
||||
|
||||
@Test
|
||||
public void modifyRequestHeaders() {
|
||||
StandardOperationRequest request = new StandardOperationRequest(
|
||||
URI.create("http://localhost"), HttpMethod.GET, new byte[0],
|
||||
getHttpHeaders(), new Parameters(),
|
||||
Collections.<OperationRequestPart> emptyList());
|
||||
OperationRequest preprocessed = this.preprocessor.preprocess(request);
|
||||
assertThat(preprocessed.getHeaders().size(), is(equalTo(1)));
|
||||
assertThat(preprocessed.getHeaders(), hasEntry("a", Arrays.asList("alpha")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modifyResponseHeaders() {
|
||||
StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK,
|
||||
getHttpHeaders(), new byte[0]);
|
||||
OperationResponse preprocessed = this.preprocessor.preprocess(response);
|
||||
assertThat(preprocessed.getHeaders().size(), is(equalTo(1)));
|
||||
assertThat(preprocessed.getHeaders(), hasEntry("a", Arrays.asList("alpha")));
|
||||
}
|
||||
|
||||
private HttpHeaders getHttpHeaders() {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("a", "alpha");
|
||||
httpHeaders.add("b", "bravo");
|
||||
httpHeaders.add("b", "banana");
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.operation.preprocess;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.restdocs.hypermedia.Link;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
/**
|
||||
* Tests for {@link LinkMaskingContentModifier}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class LinkMaskingContentModifierTests {
|
||||
|
||||
private final ContentModifier contentModifier = new LinkMaskingContentModifier();
|
||||
|
||||
private final Link[] links = new Link[] { new Link("a", "alpha"),
|
||||
new Link("b", "bravo") };
|
||||
|
||||
private final Link[] maskedLinks = new Link[] { new Link("a", "..."),
|
||||
new Link("b", "...") };
|
||||
|
||||
@Test
|
||||
public void halLinksAreMasked() throws Exception {
|
||||
assertThat(this.contentModifier.modifyContent(halPayloadWithLinks(this.links)),
|
||||
is(equalTo(halPayloadWithLinks(this.maskedLinks))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formattedHalLinksAreMasked() throws Exception {
|
||||
assertThat(
|
||||
this.contentModifier
|
||||
.modifyContent(formattedHalPayloadWithLinks(this.links)),
|
||||
is(equalTo(formattedHalPayloadWithLinks(this.maskedLinks))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void atomLinksAreMasked() throws Exception {
|
||||
assertThat(this.contentModifier.modifyContent(atomPayloadWithLinks(this.links)),
|
||||
is(equalTo(atomPayloadWithLinks(this.maskedLinks))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formattedAtomLinksAreMasked() throws Exception {
|
||||
assertThat(
|
||||
this.contentModifier
|
||||
.modifyContent(formattedAtomPayloadWithLinks(this.links)),
|
||||
is(equalTo(formattedAtomPayloadWithLinks(this.maskedLinks))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maskCanBeCustomized() throws Exception {
|
||||
assertThat(
|
||||
new LinkMaskingContentModifier("custom")
|
||||
.modifyContent(formattedAtomPayloadWithLinks(this.links)),
|
||||
is(equalTo(formattedAtomPayloadWithLinks(new Link("a", "custom"),
|
||||
new Link("b", "custom")))));
|
||||
}
|
||||
|
||||
private byte[] atomPayloadWithLinks(Link... links) throws JsonProcessingException {
|
||||
return new ObjectMapper().writeValueAsBytes(createAtomPayload(links));
|
||||
}
|
||||
|
||||
private byte[] formattedAtomPayloadWithLinks(Link... links)
|
||||
throws JsonProcessingException {
|
||||
return new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true)
|
||||
.writeValueAsBytes(createAtomPayload(links));
|
||||
}
|
||||
|
||||
private AtomPayload createAtomPayload(Link... links) {
|
||||
AtomPayload payload = new AtomPayload();
|
||||
payload.setLinks(Arrays.asList(links));
|
||||
return payload;
|
||||
}
|
||||
|
||||
private byte[] halPayloadWithLinks(Link... links) throws JsonProcessingException {
|
||||
return new ObjectMapper().writeValueAsBytes(createHalPayload(links));
|
||||
}
|
||||
|
||||
private byte[] formattedHalPayloadWithLinks(Link... links)
|
||||
throws JsonProcessingException {
|
||||
return new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true)
|
||||
.writeValueAsBytes(createHalPayload(links));
|
||||
}
|
||||
|
||||
private HalPayload createHalPayload(Link... links) {
|
||||
HalPayload payload = new HalPayload();
|
||||
Map<String, Object> linksMap = new LinkedHashMap<>();
|
||||
for (Link link : links) {
|
||||
Map<String, String> linkMap = new HashMap<>();
|
||||
linkMap.put("href", link.getHref());
|
||||
linksMap.put(link.getRel(), linkMap);
|
||||
}
|
||||
payload.setLinks(linksMap);
|
||||
return payload;
|
||||
}
|
||||
|
||||
static final class AtomPayload {
|
||||
|
||||
private List<Link> links;
|
||||
|
||||
public void setLinks(List<Link> links) {
|
||||
this.links = links;
|
||||
}
|
||||
|
||||
public List<Link> getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class HalPayload {
|
||||
|
||||
private Map<String, Object> links;
|
||||
|
||||
@JsonProperty("_links")
|
||||
public Map<String, Object> getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
public void setLinks(Map<String, Object> links) {
|
||||
this.links = links;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.operation.preprocess;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link PatternReplacingContentModifier}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class PatternReplacingContentModifierTests {
|
||||
|
||||
@Test
|
||||
public void patternsAreReplaced() throws Exception {
|
||||
Pattern pattern = Pattern.compile(
|
||||
"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(
|
||||
pattern, "<<uuid>>");
|
||||
assertThat(
|
||||
contentModifier.modifyContent("{\"id\" : \"CA761232-ED42-11CE-BACD-00AA0057B223\"}"
|
||||
.getBytes()), is(equalTo("{\"id\" : \"<<uuid>>\"}".getBytes())));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.operation.preprocess;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link PrettyPrintingContentModifier}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class PrettyPrintingContentModifierTests {
|
||||
|
||||
@Test
|
||||
public void prettyPrintJson() throws Exception {
|
||||
assertThat(
|
||||
new PrettyPrintingContentModifier().modifyContent("{\"a\":5}".getBytes()),
|
||||
equalTo(String.format("{%n \"a\" : 5%n}").getBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prettyPrintXml() throws Exception {
|
||||
assertThat(
|
||||
new PrettyPrintingContentModifier().modifyContent("<one a=\"alpha\"><two b=\"bravo\"/></one>"
|
||||
.getBytes()), equalTo(String.format(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n"
|
||||
+ "<one a=\"alpha\">%n <two b=\"bravo\"/>%n</one>%n")
|
||||
.getBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empytContentIsHandledGracefully() throws Exception {
|
||||
assertThat(new PrettyPrintingContentModifier().modifyContent("".getBytes()),
|
||||
equalTo("".getBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonJsonAndNonXmlContentIsHandledGracefully() throws Exception {
|
||||
String content = "abcdefg";
|
||||
assertThat(new PrettyPrintingContentModifier().modifyContent(content.getBytes()),
|
||||
equalTo(content.getBytes()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.payload;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonFieldPath}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class JsonFieldPathTests {
|
||||
|
||||
@Test
|
||||
public void singleFieldIsPrecise() {
|
||||
assertTrue(JsonFieldPath.compile("a").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleNestedFieldIsPrecise() {
|
||||
assertTrue(JsonFieldPath.compile("a.b").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topLevelArrayIsNotPrecise() {
|
||||
assertFalse(JsonFieldPath.compile("[]").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldBeneathTopLevelArrayIsNotPrecise() {
|
||||
assertFalse(JsonFieldPath.compile("[]a").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayIsNotPrecise() {
|
||||
assertFalse(JsonFieldPath.compile("a[]").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedArrayIsNotPrecise() {
|
||||
assertFalse(JsonFieldPath.compile("a.b[]").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayOfArraysIsNotPrecise() {
|
||||
assertFalse(JsonFieldPath.compile("a[][]").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldBeneathAnArrayIsNotPrecise() {
|
||||
assertFalse(JsonFieldPath.compile("a[].b").isPrecise());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilationOfSingleElementPath() {
|
||||
assertThat(JsonFieldPath.compile("a").getSegments(), contains("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilationOfMultipleElementPath() {
|
||||
assertThat(JsonFieldPath.compile("a.b.c").getSegments(), contains("a", "b", "c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilationOfPathWithArraysWithNoDotSeparators() {
|
||||
assertThat(JsonFieldPath.compile("a[]b[]c").getSegments(),
|
||||
contains("a", "[]", "b", "[]", "c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilationOfPathWithArraysWithPreAndPostDotSeparators() {
|
||||
assertThat(JsonFieldPath.compile("a.[].b.[].c").getSegments(),
|
||||
contains("a", "[]", "b", "[]", "c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilationOfPathWithArraysWithPreDotSeparators() {
|
||||
assertThat(JsonFieldPath.compile("a.[]b.[]c").getSegments(),
|
||||
contains("a", "[]", "b", "[]", "c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilationOfPathWithArraysWithPostDotSeparators() {
|
||||
assertThat(JsonFieldPath.compile("a[].b[].c").getSegments(),
|
||||
contains("a", "[]", "b", "[]", "c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilationOfPathStartingWithAnArray() {
|
||||
assertThat(JsonFieldPath.compile("[]a.b.c").getSegments(),
|
||||
contains("[]", "a", "b", "c"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.payload;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonFieldProcessor}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class JsonFieldProcessorTests {
|
||||
|
||||
private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor();
|
||||
|
||||
@Test
|
||||
public void extractTopLevelMapEntry() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("a", "alpha");
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a"), payload),
|
||||
equalTo((Object) "alpha"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractNestedMapEntry() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, Object> alpha = new HashMap<>();
|
||||
payload.put("a", alpha);
|
||||
alpha.put("b", "bravo");
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a.b"), payload),
|
||||
equalTo((Object) "bravo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractArray() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, Object> bravo = new HashMap<>();
|
||||
bravo.put("b", "bravo");
|
||||
List<Map<String, Object>> alpha = Arrays.asList(bravo, bravo);
|
||||
payload.put("a", alpha);
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a"), payload),
|
||||
equalTo((Object) alpha));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractArrayContents() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, Object> bravo = new HashMap<>();
|
||||
bravo.put("b", "bravo");
|
||||
List<Map<String, Object>> alpha = Arrays.asList(bravo, bravo);
|
||||
payload.put("a", alpha);
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[]"), payload),
|
||||
equalTo((Object) alpha));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractFromItemsInArray() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, Object> entry = new HashMap<>();
|
||||
entry.put("b", "bravo");
|
||||
List<Map<String, Object>> alpha = Arrays.asList(entry, entry);
|
||||
payload.put("a", alpha);
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[].b"), payload),
|
||||
equalTo((Object) Arrays.asList("bravo", "bravo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractNestedArray() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, String> entry1 = createEntry("id:1");
|
||||
Map<String, String> entry2 = createEntry("id:2");
|
||||
Map<String, String> entry3 = createEntry("id:3");
|
||||
List<List<Map<String, String>>> alpha = Arrays.asList(
|
||||
Arrays.asList(entry1, entry2), Arrays.asList(entry3));
|
||||
payload.put("a", alpha);
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[][]"), payload),
|
||||
equalTo((Object) Arrays.asList(entry1, entry2, entry3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractFromItemsInNestedArray() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, String> entry1 = createEntry("id:1");
|
||||
Map<String, String> entry2 = createEntry("id:2");
|
||||
Map<String, String> entry3 = createEntry("id:3");
|
||||
List<List<Map<String, String>>> alpha = Arrays.asList(
|
||||
Arrays.asList(entry1, entry2), Arrays.asList(entry3));
|
||||
payload.put("a", alpha);
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[][].id"), payload),
|
||||
equalTo((Object) Arrays.asList("1", "2", "3")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractArraysFromItemsInNestedArray() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, Object> entry1 = createEntry("ids", Arrays.asList(1, 2));
|
||||
Map<String, Object> entry2 = createEntry("ids", Arrays.asList(3));
|
||||
Map<String, Object> entry3 = createEntry("ids", Arrays.asList(4));
|
||||
List<List<Map<String, Object>>> alpha = Arrays.asList(
|
||||
Arrays.asList(entry1, entry2), Arrays.asList(entry3));
|
||||
payload.put("a", alpha);
|
||||
assertThat(this.fieldProcessor.extract(JsonFieldPath.compile("a[][].ids"), payload),
|
||||
equalTo((Object) Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3),
|
||||
Arrays.asList(4))));
|
||||
}
|
||||
|
||||
@Test(expected = FieldDoesNotExistException.class)
|
||||
public void nonExistentTopLevelField() {
|
||||
this.fieldProcessor
|
||||
.extract(JsonFieldPath.compile("a"), new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
@Test(expected = FieldDoesNotExistException.class)
|
||||
public void nonExistentNestedField() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("a", new HashMap<String, Object>());
|
||||
this.fieldProcessor.extract(JsonFieldPath.compile("a.b"), payload);
|
||||
}
|
||||
|
||||
@Test(expected = FieldDoesNotExistException.class)
|
||||
public void nonExistentNestedFieldWhenParentIsNotAMap() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("a", 5);
|
||||
this.fieldProcessor.extract(JsonFieldPath.compile("a.b"), payload);
|
||||
}
|
||||
|
||||
@Test(expected = FieldDoesNotExistException.class)
|
||||
public void nonExistentFieldWhenParentIsAnArray() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
HashMap<String, Object> alpha = new HashMap<String, Object>();
|
||||
alpha.put("b", Arrays.asList(new HashMap<String, Object>()));
|
||||
payload.put("a", alpha);
|
||||
this.fieldProcessor.extract(JsonFieldPath.compile("a.b.c"), payload);
|
||||
}
|
||||
|
||||
@Test(expected = FieldDoesNotExistException.class)
|
||||
public void nonExistentArrayField() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
this.fieldProcessor.extract(JsonFieldPath.compile("a[]"), payload);
|
||||
}
|
||||
|
||||
@Test(expected = FieldDoesNotExistException.class)
|
||||
public void nonExistentArrayFieldAsTypeDoesNotMatch() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put("a", 5);
|
||||
this.fieldProcessor.extract(JsonFieldPath.compile("a[]"), payload);
|
||||
}
|
||||
|
||||
@Test(expected = FieldDoesNotExistException.class)
|
||||
public void nonExistentFieldBeneathAnArray() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
HashMap<String, Object> alpha = new HashMap<String, Object>();
|
||||
alpha.put("b", Arrays.asList(new HashMap<String, Object>()));
|
||||
payload.put("a", alpha);
|
||||
this.fieldProcessor.extract(JsonFieldPath.compile("a.b[].id"), payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeTopLevelMapEntry() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("a", "alpha");
|
||||
this.fieldProcessor.remove(JsonFieldPath.compile("a"), payload);
|
||||
assertThat(payload.size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeNestedMapEntry() {
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, Object> alpha = new HashMap<>();
|
||||
payload.put("a", alpha);
|
||||
alpha.put("b", "bravo");
|
||||
this.fieldProcessor.remove(JsonFieldPath.compile("a.b"), payload);
|
||||
assertThat(payload.size(), equalTo(0));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void removeItemsInArray() throws IOException {
|
||||
Map<String, Object> payload = new ObjectMapper().readValue(
|
||||
"{\"a\": [{\"b\":\"bravo\"},{\"b\":\"bravo\"}]}", Map.class);
|
||||
this.fieldProcessor.remove(JsonFieldPath.compile("a[].b"), payload);
|
||||
assertThat(payload.size(), equalTo(0));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void removeItemsInNestedArray() throws IOException {
|
||||
Map<String, Object> payload = new ObjectMapper().readValue(
|
||||
"{\"a\": [[{\"id\":1},{\"id\":2}], [{\"id\":3}]]}", Map.class);
|
||||
this.fieldProcessor.remove(JsonFieldPath.compile("a[][].id"), payload);
|
||||
assertThat(payload.size(), equalTo(0));
|
||||
}
|
||||
|
||||
private Map<String, String> createEntry(String... pairs) {
|
||||
Map<String, String> entry = new HashMap<>();
|
||||
for (String pair : pairs) {
|
||||
String[] components = pair.split(":");
|
||||
entry.put(components[0], components[1]);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private Map<String, Object> createEntry(String key, Object value) {
|
||||
Map<String, Object> entry = new HashMap<>();
|
||||
entry.put(key, value);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.payload;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonFieldTypeResolver}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class JsonFieldTypeResolverTests {
|
||||
|
||||
private final JsonFieldTypeResolver fieldTypeResolver = new JsonFieldTypeResolver();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrownException = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void arrayField() throws IOException {
|
||||
assertFieldType(JsonFieldType.ARRAY, "[]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void booleanField() throws IOException {
|
||||
assertFieldType(JsonFieldType.BOOLEAN, "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void objectField() throws IOException {
|
||||
assertFieldType(JsonFieldType.OBJECT, "{}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullField() throws IOException {
|
||||
assertFieldType(JsonFieldType.NULL, "null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numberField() throws IOException {
|
||||
assertFieldType(JsonFieldType.NUMBER, "1.2345");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringField() throws IOException {
|
||||
assertFieldType(JsonFieldType.STRING, "\"Foo\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedField() throws IOException {
|
||||
assertThat(this.fieldTypeResolver.resolveFieldType("a.b.c",
|
||||
createPayload("{\"a\":{\"b\":{\"c\":{}}}}")), equalTo(JsonFieldType.OBJECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleFieldsWithSameType() throws IOException {
|
||||
assertThat(this.fieldTypeResolver.resolveFieldType("a[].id",
|
||||
createPayload("{\"a\":[{\"id\":1},{\"id\":2}]}")),
|
||||
equalTo(JsonFieldType.NUMBER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleFieldsWithDifferentTypes() throws IOException {
|
||||
assertThat(this.fieldTypeResolver.resolveFieldType("a[].id",
|
||||
createPayload("{\"a\":[{\"id\":1},{\"id\":true}]}")),
|
||||
equalTo(JsonFieldType.VARIES));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonExistentFieldProducesIllegalArgumentException() throws IOException {
|
||||
this.thrownException.expect(FieldDoesNotExistException.class);
|
||||
this.thrownException
|
||||
.expectMessage("The payload does not contain a field with the path 'a.b'");
|
||||
this.fieldTypeResolver.resolveFieldType("a.b", createPayload("{\"a\":{}}"));
|
||||
}
|
||||
|
||||
private void assertFieldType(JsonFieldType expectedType, String jsonValue)
|
||||
throws IOException {
|
||||
assertThat(this.fieldTypeResolver.resolveFieldType("field",
|
||||
createSimplePayload(jsonValue)), equalTo(expectedType));
|
||||
}
|
||||
|
||||
private Map<String, Object> createSimplePayload(String value) throws IOException {
|
||||
return createPayload("{\"field\":" + value + "}");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> createPayload(String json) throws IOException {
|
||||
return new ObjectMapper().readValue(json, Map.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.payload;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.endsWith;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
|
||||
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.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link RequestFieldsSnippet}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestFieldsSnippetTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Rule
|
||||
public final ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Test
|
||||
public void mapRequestWithFields() throws IOException {
|
||||
this.snippet.expectRequestFields("map-request-with-fields").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description") //
|
||||
.row("a.b", "Number", "one") //
|
||||
.row("a.c", "String", "two") //
|
||||
.row("a", "Object", "three"));
|
||||
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"),
|
||||
fieldWithPath("a.c").description("two"),
|
||||
fieldWithPath("a").description("three"))).document(new OperationBuilder(
|
||||
"map-request-with-fields", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost")
|
||||
.content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayRequestWithFields() throws IOException {
|
||||
this.snippet.expectRequestFields("array-request-with-fields").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description") //
|
||||
.row("[]a.b", "Number", "one") //
|
||||
.row("[]a.c", "String", "two") //
|
||||
.row("[]a", "Object", "three"));
|
||||
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"),
|
||||
fieldWithPath("[]a.c").description("two"), fieldWithPath("[]a")
|
||||
.description("three"))).document(new OperationBuilder(
|
||||
"array-request-with-fields", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost")
|
||||
.content("[{\"a\": {\"b\": 5}},{\"a\": {\"c\": \"charlie\"}}]").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedRequestField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(startsWith("The following parts of the payload were not"
|
||||
+ " documented:"));
|
||||
new RequestFieldsSnippet(Collections.<FieldDescriptor> emptyList())
|
||||
.document(new OperationBuilder("undocumented-request-field", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost")
|
||||
.content("{\"a\": 5}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingRequestField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Fields with the following paths were not found"
|
||||
+ " in the payload: [a.b]"));
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")))
|
||||
.document(new OperationBuilder("missing-request-fields", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost").content("{}")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingOptionalRequestFieldWithNoTypeProvided() throws IOException {
|
||||
this.thrown.expect(FieldTypeRequiredException.class);
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")
|
||||
.optional())).document(new OperationBuilder(
|
||||
"missing-optional-request-field-with-no-type", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost").content("{ }")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedRequestFieldAndMissingRequestField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(startsWith("The following parts of the payload were not"
|
||||
+ " documented:"));
|
||||
this.thrown
|
||||
.expectMessage(endsWith("Fields with the following paths were not found"
|
||||
+ " in the payload: [a.b]"));
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")))
|
||||
.document(new OperationBuilder(
|
||||
"undocumented-request-field-and-missing-request-field",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost")
|
||||
.content("{ \"a\": { \"c\": 5 }}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestFieldsWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectRequestFields(
|
||||
"request-fields-with-custom-descriptor-attributes").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description", "Foo") //
|
||||
.row("a.b", "Number", "one", "alpha") //
|
||||
.row("a.c", "String", "two", "bravo") //
|
||||
.row("a", "Object", "three", "charlie"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("request-fields")).thenReturn(
|
||||
snippetResource("request-fields-with-extra-column"));
|
||||
new RequestFieldsSnippet(Arrays.asList(
|
||||
fieldWithPath("a.b").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
fieldWithPath("a.c").description("two").attributes(
|
||||
key("foo").value("bravo")),
|
||||
fieldWithPath("a").description("three").attributes(
|
||||
key("foo").value("charlie")))).document(new OperationBuilder(
|
||||
"request-fields-with-custom-descriptor-attributes", this.snippet
|
||||
.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).request("http://localhost")
|
||||
.content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestFieldsWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectRequestFields("request-fields-with-custom-attributes")
|
||||
.withContents(startsWith(".Custom title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("request-fields")).thenReturn(
|
||||
snippetResource("request-fields-with-title"));
|
||||
new RequestFieldsSnippet(attributes(key("title").value("Custom title")),
|
||||
Arrays.asList(fieldWithPath("a").description("one")))
|
||||
.document(new OperationBuilder("request-fields-with-custom-attributes",
|
||||
this.snippet.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost").content("{\"a\": \"foo\"}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlRequestFields() throws IOException {
|
||||
this.snippet.expectRequestFields("xml-request").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description") //
|
||||
.row("a/b", "b", "one") //
|
||||
.row("a/c", "c", "two") //
|
||||
.row("a", "a", "three"));
|
||||
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")
|
||||
.type("b"), fieldWithPath("a/c").description("two").type("c"),
|
||||
fieldWithPath("a").description("three").type("a")))
|
||||
.document(new OperationBuilder("xml-request", this.snippet
|
||||
.getOutputDirectory())
|
||||
.request("http://localhost")
|
||||
.content("<a><b>5</b><c>charlie</c></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedXmlRequestField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(startsWith("The following parts of the payload were not"
|
||||
+ " documented:"));
|
||||
new RequestFieldsSnippet(Collections.<FieldDescriptor> emptyList())
|
||||
.document(new OperationBuilder("undocumented-xml-request-field",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost")
|
||||
.content("<a><b>5</b></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlRequestFieldWithNoType() throws IOException {
|
||||
this.thrown.expect(FieldTypeRequiredException.class);
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")))
|
||||
.document(new OperationBuilder("missing-xml-request", this.snippet
|
||||
.getOutputDirectory())
|
||||
.request("http://localhost")
|
||||
.content("<a>5</a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingXmlRequestField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Fields with the following paths were not found"
|
||||
+ " in the payload: [a/b]"));
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"),
|
||||
fieldWithPath("a").description("one"))).document(new OperationBuilder(
|
||||
"missing-xml-request-fields", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost").content("<a></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedXmlRequestFieldAndMissingXmlRequestField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(startsWith("The following parts of the payload were not"
|
||||
+ " documented:"));
|
||||
this.thrown
|
||||
.expectMessage(endsWith("Fields with the following paths were not found"
|
||||
+ " in the payload: [a/b]"));
|
||||
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")))
|
||||
.document(new OperationBuilder(
|
||||
"undocumented-xml-request-field-and-missing-xml-request-field",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost")
|
||||
.content("<a><c>5</c></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
private FileSystemResource snippetResource(String name) {
|
||||
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
|
||||
+ name + ".snippet");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.payload;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.endsWith;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
|
||||
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.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReponseFieldsSnippet}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ResponseFieldsSnippetTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Rule
|
||||
public final ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Test
|
||||
public void mapResponseWithFields() throws IOException {
|
||||
this.snippet.expectResponseFields("map-response-with-fields").withContents(//
|
||||
tableWithHeader("Path", "Type", "Description") //
|
||||
.row("id", "Number", "one") //
|
||||
.row("date", "String", "two") //
|
||||
.row("assets", "Array", "three") //
|
||||
.row("assets[]", "Object", "four") //
|
||||
.row("assets[].id", "Number", "five") //
|
||||
.row("assets[].name", "String", "six"));
|
||||
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("id").description("one"),
|
||||
fieldWithPath("date").description("two"), fieldWithPath("assets")
|
||||
.description("three"),
|
||||
fieldWithPath("assets[]").description("four"),
|
||||
fieldWithPath("assets[].id").description("five"),
|
||||
fieldWithPath("assets[].name").description("six")))
|
||||
.document(new OperationBuilder("map-response-with-fields", this.snippet
|
||||
.getOutputDirectory())
|
||||
.response()
|
||||
.content(
|
||||
"{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":"
|
||||
+ " [{\"id\":356,\"name\": \"sample\"}]}")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayResponseWithFields() throws IOException {
|
||||
this.snippet.expectResponseFields("array-response-with-fields").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description") //
|
||||
.row("[]a.b", "Number", "one") //
|
||||
.row("[]a.c", "String", "two") //
|
||||
.row("[]a", "Object", "three"));
|
||||
new ResponseFieldsSnippet(Arrays.asList(
|
||||
fieldWithPath("[]a.b").description("one"), fieldWithPath("[]a.c")
|
||||
.description("two"), fieldWithPath("[]a").description("three")))
|
||||
.document(new OperationBuilder("array-response-with-fields", this.snippet
|
||||
.getOutputDirectory()).response()
|
||||
.content("[{\"a\": {\"b\": 5}},{\"a\": {\"c\": \"charlie\"}}]")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayResponse() throws IOException {
|
||||
this.snippet.expectResponseFields("array-response").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description") //
|
||||
.row("[]", "String", "one"));
|
||||
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]").description("one")))
|
||||
.document(new OperationBuilder("array-response", this.snippet
|
||||
.getOutputDirectory()).response()
|
||||
.content("[\"a\", \"b\", \"c\"]").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseFieldsWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectResponseFields("response-fields-with-custom-attributes")
|
||||
.withContents( //
|
||||
tableWithHeader("Path", "Type", "Description", "Foo") //
|
||||
.row("a.b", "Number", "one", "alpha") //
|
||||
.row("a.c", "String", "two", "bravo") //
|
||||
.row("a", "Object", "three", "charlie"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("response-fields")).thenReturn(
|
||||
snippetResource("response-fields-with-extra-column"));
|
||||
new ResponseFieldsSnippet(Arrays.asList(
|
||||
fieldWithPath("a.b").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
fieldWithPath("a.c").description("two").attributes(
|
||||
key("foo").value("bravo")),
|
||||
fieldWithPath("a").description("three").attributes(
|
||||
key("foo").value("charlie")))).document(new OperationBuilder(
|
||||
"response-fields-with-custom-attributes", this.snippet
|
||||
.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).response()
|
||||
.content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseFieldsWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectResponseFields("response-fields-with-custom-attributes")
|
||||
.withContents(startsWith(".Custom title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("response-fields")).thenReturn(
|
||||
snippetResource("response-fields-with-title"));
|
||||
new ResponseFieldsSnippet(attributes(key("title").value("Custom title")),
|
||||
Arrays.asList(fieldWithPath("a").description("one")))
|
||||
.document(new OperationBuilder("response-fields-with-custom-attributes",
|
||||
this.snippet.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).response()
|
||||
.content("{\"a\": \"foo\"}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlResponseFields() throws IOException {
|
||||
this.snippet.expectResponseFields("xml-response").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description") //
|
||||
.row("a/b", "b", "one") //
|
||||
.row("a/c", "c", "two") //
|
||||
.row("a", "a", "three"));
|
||||
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")
|
||||
.type("b"), fieldWithPath("a/c").description("two").type("c"),
|
||||
fieldWithPath("a").description("three").type("a")))
|
||||
.document(new OperationBuilder("xml-response", this.snippet
|
||||
.getOutputDirectory())
|
||||
.response()
|
||||
.content("<a><b>5</b><c>charlie</c></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedXmlResponseField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(startsWith("The following parts of the payload were not"
|
||||
+ " documented:"));
|
||||
new ResponseFieldsSnippet(Collections.<FieldDescriptor> emptyList())
|
||||
.document(new OperationBuilder("undocumented-xml-response-field",
|
||||
this.snippet.getOutputDirectory())
|
||||
.response()
|
||||
.content("<a><b>5</b></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlResponseFieldWithNoType() throws IOException {
|
||||
this.thrown.expect(FieldTypeRequiredException.class);
|
||||
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")))
|
||||
.document(new OperationBuilder("xml-response-no-field-type", this.snippet
|
||||
.getOutputDirectory())
|
||||
.response()
|
||||
.content("<a>5</a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingXmlResponseField() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Fields with the following paths were not found"
|
||||
+ " in the payload: [a/b]"));
|
||||
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"),
|
||||
fieldWithPath("a").description("one"))).document(new OperationBuilder(
|
||||
"missing-xml-response-field", this.snippet.getOutputDirectory())
|
||||
.response().content("<a></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedXmlResponseFieldAndMissingXmlResponseField()
|
||||
throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(startsWith("The following parts of the payload were not"
|
||||
+ " documented:"));
|
||||
this.thrown
|
||||
.expectMessage(endsWith("Fields with the following paths were not found"
|
||||
+ " in the payload: [a/b]"));
|
||||
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")))
|
||||
.document(new OperationBuilder(
|
||||
"undocumented-xml-request-field-and-missing-xml-request-field",
|
||||
this.snippet.getOutputDirectory())
|
||||
.response()
|
||||
.content("<a><c>5</c></a>")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
private FileSystemResource snippetResource(String name) {
|
||||
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
|
||||
+ name + ".snippet");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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 static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithTitleAndHeader;
|
||||
|
||||
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.core.io.FileSystemResource;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link PathParametersSnippet}
|
||||
*
|
||||
* @author awilkinson
|
||||
*
|
||||
*/
|
||||
public class PathParametersSnippetTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Rule
|
||||
public ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Test
|
||||
public void undocumentedPathParameter() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
|
||||
+ " not documented: [a]"));
|
||||
new PathParametersSnippet(Collections.<ParameterDescriptor> emptyList())
|
||||
.document(new OperationBuilder("undocumented-path-parameter",
|
||||
this.snippet.getOutputDirectory()).attribute(
|
||||
"org.springframework.restdocs.urlTemplate", "/{a}/").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingPathParameter() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
|
||||
+ " not found in the request: [a]"));
|
||||
new PathParametersSnippet(
|
||||
Arrays.asList(parameterWithName("a").description("one")))
|
||||
.document(new OperationBuilder("missing-path-parameter", this.snippet
|
||||
.getOutputDirectory()).attribute(
|
||||
"org.springframework.restdocs.urlTemplate", "/").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedAndMissingPathParameters() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
|
||||
+ " not documented: [b]. Path parameters with the following"
|
||||
+ " names were not found in the request: [a]"));
|
||||
new PathParametersSnippet(
|
||||
Arrays.asList(parameterWithName("a").description("one")))
|
||||
.document(new OperationBuilder(
|
||||
"undocumented-and-missing-path-parameters", this.snippet
|
||||
.getOutputDirectory()).attribute(
|
||||
"org.springframework.restdocs.urlTemplate", "/{b}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathParameters() throws IOException {
|
||||
this.snippet.expectPathParameters("path-parameters").withContents(
|
||||
tableWithTitleAndHeader("/{a}/{b}", "Parameter", "Description").row("a",
|
||||
"one").row("b", "two"));
|
||||
new PathParametersSnippet(Arrays.asList(
|
||||
parameterWithName("a").description("one"), parameterWithName("b")
|
||||
.description("two"))).document(new OperationBuilder(
|
||||
"path-parameters", this.snippet.getOutputDirectory()).attribute(
|
||||
"org.springframework.restdocs.urlTemplate", "/{a}/{b}").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathParametersWithQueryString() throws IOException {
|
||||
this.snippet.expectPathParameters("path-parameters-with-query-string")
|
||||
.withContents(
|
||||
tableWithTitleAndHeader("/{a}/{b}", "Parameter", "Description")
|
||||
.row("a", "one").row("b", "two"));
|
||||
new PathParametersSnippet(Arrays.asList(
|
||||
parameterWithName("a").description("one"), parameterWithName("b")
|
||||
.description("two")))
|
||||
.document(new OperationBuilder("path-parameters-with-query-string",
|
||||
this.snippet.getOutputDirectory()).attribute(
|
||||
"org.springframework.restdocs.urlTemplate", "/{a}/{b}?foo=bar")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathParametersWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectPathParameters(
|
||||
"path-parameters-with-custom-descriptor-attributes").withContents(
|
||||
tableWithHeader("Parameter", "Description", "Foo").row("a", "one",
|
||||
"alpha").row("b", "two", "bravo"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("path-parameters")).thenReturn(
|
||||
snippetResource("path-parameters-with-extra-column"));
|
||||
new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one")
|
||||
.attributes(key("foo").value("alpha")), parameterWithName("b")
|
||||
.description("two").attributes(key("foo").value("bravo"))))
|
||||
.document(new OperationBuilder(
|
||||
"path-parameters-with-custom-descriptor-attributes", this.snippet
|
||||
.getOutputDirectory())
|
||||
.attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}")
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathParametersWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectPathParameters("path-parameters-with-custom-attributes")
|
||||
.withContents(startsWith(".The title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("path-parameters")).thenReturn(
|
||||
snippetResource("path-parameters-with-title"));
|
||||
new PathParametersSnippet(
|
||||
attributes(key("title").value("The title")),
|
||||
Arrays.asList(
|
||||
parameterWithName("a").description("one").attributes(
|
||||
key("foo").value("alpha")), parameterWithName("b")
|
||||
.description("two").attributes(key("foo").value("bravo"))))
|
||||
.document(new OperationBuilder("path-parameters-with-custom-attributes",
|
||||
this.snippet.getOutputDirectory())
|
||||
.attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}")
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).build());
|
||||
|
||||
}
|
||||
|
||||
private FileSystemResource snippetResource(String name) {
|
||||
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
|
||||
+ name + ".snippet");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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 static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
|
||||
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.core.io.FileSystemResource;
|
||||
import org.springframework.restdocs.snippet.SnippetException;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
import org.springframework.restdocs.test.OperationBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link RequestParametersSnippet}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RequestParametersSnippetTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Rule
|
||||
public ExpectedSnippet snippet = new ExpectedSnippet();
|
||||
|
||||
@Test
|
||||
public void undocumentedParameter() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Request parameters with the following names were"
|
||||
+ " not documented: [a]"));
|
||||
new RequestParametersSnippet(Collections.<ParameterDescriptor> emptyList())
|
||||
.document(new OperationBuilder("undocumented-parameter", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost")
|
||||
.param("a", "alpha").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingParameter() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Request parameters with the following names were"
|
||||
+ " not found in the request: [a]"));
|
||||
new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description(
|
||||
"one"))).document(new OperationBuilder("missing-parameter", this.snippet
|
||||
.getOutputDirectory()).request("http://localhost").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undocumentedAndMissingParameters() throws IOException {
|
||||
this.thrown.expect(SnippetException.class);
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Request parameters with the following names were"
|
||||
+ " not documented: [b]. Request parameters with the following"
|
||||
+ " names were not found in the request: [a]"));
|
||||
new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description(
|
||||
"one"))).document(new OperationBuilder(
|
||||
"undocumented-and-missing-parameters", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost").param("b", "bravo").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestParameters() throws IOException {
|
||||
this.snippet.expectRequestParameters("request-parameters").withContents(
|
||||
tableWithHeader("Parameter", "Description").row("a", "one").row("b",
|
||||
"two"));
|
||||
new RequestParametersSnippet(Arrays.asList(
|
||||
parameterWithName("a").description("one"), parameterWithName("b")
|
||||
.description("two"))).document(new OperationBuilder(
|
||||
"request-parameters", this.snippet.getOutputDirectory())
|
||||
.request("http://localhost").param("a", "bravo").param("b", "bravo")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestParametersWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectRequestParameters(
|
||||
"request-parameters-with-custom-descriptor-attributes").withContents(
|
||||
tableWithHeader("Parameter", "Description", "Foo").row("a", "one",
|
||||
"alpha").row("b", "two", "bravo"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("request-parameters")).thenReturn(
|
||||
snippetResource("request-parameters-with-extra-column"));
|
||||
new RequestParametersSnippet(Arrays.asList(
|
||||
parameterWithName("a").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
parameterWithName("b").description("two").attributes(
|
||||
key("foo").value("bravo")))).document(new OperationBuilder(
|
||||
"request-parameters-with-custom-descriptor-attributes", this.snippet
|
||||
.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver)).request("http://localhost")
|
||||
.param("a", "alpha").param("b", "bravo").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestParametersWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectRequestParameters("request-parameters-with-custom-attributes")
|
||||
.withContents(startsWith(".The title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("request-parameters")).thenReturn(
|
||||
snippetResource("request-parameters-with-title"));
|
||||
new RequestParametersSnippet(
|
||||
attributes(key("title").value("The title")),
|
||||
Arrays.asList(
|
||||
parameterWithName("a").description("one").attributes(
|
||||
key("foo").value("alpha")), parameterWithName("b")
|
||||
.description("two").attributes(key("foo").value("bravo"))))
|
||||
.document(new OperationBuilder(
|
||||
"request-parameters-with-custom-attributes", this.snippet
|
||||
.getOutputDirectory())
|
||||
.attribute(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(resolver))
|
||||
.request("http://localhost").param("a", "alpha")
|
||||
.param("b", "bravo").build());
|
||||
}
|
||||
|
||||
private FileSystemResource snippetResource(String name) {
|
||||
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
|
||||
+ name + ".snippet");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.snippet;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.restdocs.RestDocumentationContext;
|
||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||
|
||||
/**
|
||||
* Tests for {@link RestDocumentationContextPlaceholderResolver}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class RestDocumentationContextPlaceholderResolverTests {
|
||||
|
||||
@Test
|
||||
public void dashSeparatedMethodName() throws Exception {
|
||||
assertThat(
|
||||
createResolver("dashSeparatedMethodName").resolvePlaceholder(
|
||||
"method-name"), equalTo("dash-separated-method-name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underscoreSeparatedMethodName() throws Exception {
|
||||
assertThat(
|
||||
createResolver("underscoreSeparatedMethodName").resolvePlaceholder(
|
||||
"method_name"), equalTo("underscore_separated_method_name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void camelCaseMethodName() throws Exception {
|
||||
assertThat(
|
||||
createResolver("camelCaseMethodName").resolvePlaceholder("methodName"),
|
||||
equalTo("camelCaseMethodName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stepCount() throws Exception {
|
||||
assertThat(createResolver("stepCount").resolvePlaceholder("step"), equalTo("0"));
|
||||
}
|
||||
|
||||
private PlaceholderResolver createResolver(String methodName) {
|
||||
return new RestDocumentationContextPlaceholderResolver(
|
||||
new RestDocumentationContext(null, methodName, null));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.snippet;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.restdocs.RestDocumentationContext;
|
||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||
|
||||
/**
|
||||
* Tests for {@link StandardWriterResolver}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class StandardWriterResolverTests {
|
||||
|
||||
private final PlaceholderResolver placeholderResolver = mock(PlaceholderResolver.class);
|
||||
|
||||
private final StandardWriterResolver resolver = new StandardWriterResolver(
|
||||
this.placeholderResolver);
|
||||
|
||||
@Test
|
||||
public void noConfiguredOutputDirectoryAndRelativeInput() {
|
||||
assertThat(this.resolver.resolveFile("foo", "bar.txt",
|
||||
new RestDocumentationContext(null, null, null)), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void absoluteInput() {
|
||||
String absolutePath = new File("foo").getAbsolutePath();
|
||||
assertThat(this.resolver.resolveFile(absolutePath, "bar.txt",
|
||||
new RestDocumentationContext(null, null, null)), is(new File(
|
||||
absolutePath, "bar.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuredOutputAndRelativeInput() {
|
||||
File outputDir = new File("foo").getAbsoluteFile();
|
||||
assertThat(this.resolver.resolveFile("bar", "baz.txt",
|
||||
new RestDocumentationContext(null, null, outputDir)), is(new File(
|
||||
outputDir, "bar/baz.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuredOutputAndAbsoluteInput() {
|
||||
File outputDir = new File("foo").getAbsoluteFile();
|
||||
String absolutePath = new File("bar").getAbsolutePath();
|
||||
assertThat(this.resolver.resolveFile(absolutePath, "baz.txt",
|
||||
new RestDocumentationContext(null, null, outputDir)), is(new File(
|
||||
absolutePath, "baz.txt")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.restdocs.snippet.TemplatedSnippet;
|
||||
import org.springframework.restdocs.test.SnippetMatchers.SnippetMatcher;
|
||||
|
||||
/**
|
||||
* The {@code ExpectedSnippet} rule is used to verify that a {@link TemplatedSnippet} has
|
||||
* generated the expected snippet.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ExpectedSnippet implements TestRule {
|
||||
|
||||
private String expectedName;
|
||||
|
||||
private String expectedType;
|
||||
|
||||
private SnippetMatcher snippet = SnippetMatchers.snippet();
|
||||
|
||||
private File outputDirectory;
|
||||
|
||||
@Override
|
||||
public Statement apply(final Statement base, Description description) {
|
||||
this.outputDirectory = new File("build/"
|
||||
+ description.getTestClass().getSimpleName());
|
||||
return new ExpectedSnippetStatement(base);
|
||||
}
|
||||
|
||||
private final class ExpectedSnippetStatement extends Statement {
|
||||
|
||||
private final Statement delegate;
|
||||
|
||||
public ExpectedSnippetStatement(Statement delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
this.delegate.evaluate();
|
||||
verifySnippet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void verifySnippet() throws IOException {
|
||||
if (this.outputDirectory != null && this.expectedName != null) {
|
||||
File snippetDir = new File(this.outputDirectory, this.expectedName);
|
||||
File snippetFile = new File(snippetDir, this.expectedType + ".adoc");
|
||||
assertThat(snippetFile, is(this.snippet));
|
||||
}
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectCurlRequest(String name) {
|
||||
expect(name, "curl-request");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectRequestFields(String name) {
|
||||
expect(name, "request-fields");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectResponseFields(String name) {
|
||||
expect(name, "response-fields");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectLinks(String name) {
|
||||
expect(name, "links");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectHttpRequest(String name) {
|
||||
expect(name, "http-request");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectHttpResponse(String name) {
|
||||
expect(name, "http-response");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectRequestParameters(String name) {
|
||||
expect(name, "request-parameters");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedSnippet expectPathParameters(String name) {
|
||||
expect(name, "path-parameters");
|
||||
return this;
|
||||
}
|
||||
|
||||
private ExpectedSnippet expect(String name, String type) {
|
||||
this.expectedName = name;
|
||||
this.expectedType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void withContents(Matcher<String> matcher) {
|
||||
this.snippet.withContents(matcher);
|
||||
}
|
||||
|
||||
public File getOutputDirectory() {
|
||||
return this.outputDirectory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.restdocs.RestDocumentationContext;
|
||||
import org.springframework.restdocs.operation.Operation;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
import org.springframework.restdocs.operation.OperationRequestPart;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.Parameters;
|
||||
import org.springframework.restdocs.operation.StandardOperation;
|
||||
import org.springframework.restdocs.operation.StandardOperationRequest;
|
||||
import org.springframework.restdocs.operation.StandardOperationRequestPart;
|
||||
import org.springframework.restdocs.operation.StandardOperationResponse;
|
||||
import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver;
|
||||
import org.springframework.restdocs.snippet.StandardWriterResolver;
|
||||
import org.springframework.restdocs.snippet.WriterResolver;
|
||||
import org.springframework.restdocs.templates.StandardTemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
|
||||
public class OperationBuilder {
|
||||
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
private final OperationResponseBuilder responseBuilder = new OperationResponseBuilder();
|
||||
|
||||
private final String name;
|
||||
|
||||
private final File outputDirectory;
|
||||
|
||||
private OperationRequestBuilder requestBuilder;
|
||||
|
||||
public OperationBuilder(String name, File outputDirectory) {
|
||||
this.name = name;
|
||||
this.outputDirectory = outputDirectory;
|
||||
}
|
||||
|
||||
public OperationRequestBuilder request(String uri) {
|
||||
this.requestBuilder = new OperationRequestBuilder(uri);
|
||||
return this.requestBuilder;
|
||||
}
|
||||
|
||||
public OperationResponseBuilder response() {
|
||||
return this.responseBuilder;
|
||||
}
|
||||
|
||||
public OperationBuilder attribute(String name, Object value) {
|
||||
this.attributes.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Operation build() {
|
||||
if (this.attributes.get(TemplateEngine.class.getName()) == null) {
|
||||
this.attributes.put(TemplateEngine.class.getName(),
|
||||
new MustacheTemplateEngine(new StandardTemplateResourceResolver()));
|
||||
}
|
||||
RestDocumentationContext context = new RestDocumentationContext(null, null,
|
||||
this.outputDirectory);
|
||||
this.attributes.put(RestDocumentationContext.class.getName(), context);
|
||||
this.attributes.put(WriterResolver.class.getName(), new StandardWriterResolver(
|
||||
new RestDocumentationContextPlaceholderResolver(context)));
|
||||
return new StandardOperation(this.name,
|
||||
(this.requestBuilder == null ? new OperationRequestBuilder(
|
||||
"http://localhost/").buildRequest() : this.requestBuilder
|
||||
.buildRequest()),
|
||||
this.responseBuilder.buildResponse(), this.attributes);
|
||||
}
|
||||
|
||||
public class OperationRequestBuilder {
|
||||
|
||||
private URI requestUri = URI.create("http://localhost/");
|
||||
|
||||
private HttpMethod method = HttpMethod.GET;
|
||||
|
||||
private byte[] content = new byte[0];
|
||||
|
||||
private HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
private Parameters parameters = new Parameters();
|
||||
|
||||
private List<OperationRequestPartBuilder> partBuilders = new ArrayList<>();
|
||||
|
||||
public OperationRequestBuilder(String uri) {
|
||||
this.requestUri = URI.create(uri);
|
||||
}
|
||||
|
||||
private OperationRequest buildRequest() {
|
||||
List<OperationRequestPart> parts = new ArrayList<>();
|
||||
for (OperationRequestPartBuilder builder : this.partBuilders) {
|
||||
parts.add(builder.buildPart());
|
||||
}
|
||||
return new StandardOperationRequest(this.requestUri, this.method,
|
||||
this.content, this.headers, this.parameters, parts);
|
||||
}
|
||||
|
||||
public Operation build() {
|
||||
return OperationBuilder.this.build();
|
||||
}
|
||||
|
||||
public OperationRequestBuilder method(String method) {
|
||||
this.method = HttpMethod.valueOf(method);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationRequestBuilder content(String content) {
|
||||
this.content = content.getBytes();
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationRequestBuilder param(String name, String... values) {
|
||||
for (String value : values) {
|
||||
this.parameters.add(name, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationRequestBuilder header(String name, String value) {
|
||||
this.headers.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationRequestPartBuilder part(String name, byte[] content) {
|
||||
OperationRequestPartBuilder partBuilder = new OperationRequestPartBuilder(
|
||||
name, content);
|
||||
this.partBuilders.add(partBuilder);
|
||||
return partBuilder;
|
||||
}
|
||||
|
||||
public class OperationRequestPartBuilder {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final byte[] content;
|
||||
|
||||
private String submittedFileName;
|
||||
|
||||
private HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
private OperationRequestPartBuilder(String name, byte[] content) {
|
||||
this.name = name;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public OperationRequestPartBuilder submittedFileName(String submittedFileName) {
|
||||
this.submittedFileName = submittedFileName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationRequestBuilder and() {
|
||||
return OperationRequestBuilder.this;
|
||||
}
|
||||
|
||||
public Operation build() {
|
||||
return OperationBuilder.this.build();
|
||||
}
|
||||
|
||||
private OperationRequestPart buildPart() {
|
||||
return new StandardOperationRequestPart(this.name,
|
||||
this.submittedFileName, this.content, this.headers);
|
||||
}
|
||||
|
||||
public OperationRequestPartBuilder header(String name, String value) {
|
||||
this.headers.add(name, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OperationResponseBuilder {
|
||||
|
||||
private HttpStatus status = HttpStatus.OK;
|
||||
|
||||
private HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
private byte[] content = new byte[0];
|
||||
|
||||
private OperationResponse buildResponse() {
|
||||
return new StandardOperationResponse(this.status, this.headers, this.content);
|
||||
}
|
||||
|
||||
public OperationResponseBuilder status(int status) {
|
||||
this.status = HttpStatus.valueOf(status);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResponseBuilder header(String name, String value) {
|
||||
this.headers.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResponseBuilder content(byte[] content) {
|
||||
this.content = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OperationResponseBuilder content(String content) {
|
||||
this.content = content.getBytes();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Operation build() {
|
||||
return OperationBuilder.this.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright 2014-2015 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.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
/**
|
||||
* {@link Matcher Matchers} for verify the contents of generated documentation snippets.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class SnippetMatchers {
|
||||
|
||||
public static SnippetMatcher snippet() {
|
||||
return new SnippetMatcher();
|
||||
}
|
||||
|
||||
public static AsciidoctorTableMatcher tableWithTitleAndHeader(String title,
|
||||
String... headers) {
|
||||
return new AsciidoctorTableMatcher(title, headers);
|
||||
}
|
||||
|
||||
public static AsciidoctorTableMatcher tableWithHeader(String... headers) {
|
||||
return new AsciidoctorTableMatcher(null, headers);
|
||||
}
|
||||
|
||||
public static HttpRequestMatcher httpRequest(RequestMethod method, String uri) {
|
||||
return new HttpRequestMatcher(method, uri);
|
||||
}
|
||||
|
||||
public static HttpResponseMatcher httpResponse(HttpStatus status) {
|
||||
return new HttpResponseMatcher(status);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
public static AsciidoctorCodeBlockMatcher<?> codeBlock(String language) {
|
||||
return new AsciidoctorCodeBlockMatcher(language);
|
||||
}
|
||||
|
||||
private static abstract class AbstractSnippetContentMatcher extends
|
||||
BaseMatcher<String> {
|
||||
|
||||
private List<String> lines = new ArrayList<String>();
|
||||
|
||||
protected void addLine(String line) {
|
||||
this.lines.add(line);
|
||||
}
|
||||
|
||||
protected void addLine(int index, String line) {
|
||||
if (index < 0) {
|
||||
index = index + this.lines.size();
|
||||
}
|
||||
this.lines.add(index, line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
return getLinesAsString().equals(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Asciidoctor snippet");
|
||||
description.appendText(getLinesAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatch(Object item, Description description) {
|
||||
description.appendText("was:");
|
||||
if (item instanceof String) {
|
||||
description.appendText((String) item);
|
||||
}
|
||||
else {
|
||||
description.appendValue(item);
|
||||
}
|
||||
}
|
||||
|
||||
private String getLinesAsString() {
|
||||
StringWriter writer = new StringWriter();
|
||||
Iterator<String> iterator = this.lines.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
writer.append(String.format("%s", iterator.next()));
|
||||
if (iterator.hasNext()) {
|
||||
writer.append(String.format("%n"));
|
||||
}
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AsciidoctorCodeBlockMatcher<T extends AsciidoctorCodeBlockMatcher<T>>
|
||||
extends AbstractSnippetContentMatcher {
|
||||
|
||||
protected AsciidoctorCodeBlockMatcher(String language) {
|
||||
this.addLine("[source," + language + "]");
|
||||
this.addLine("----");
|
||||
this.addLine("----");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T content(String content) {
|
||||
this.addLine(-1, content);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static abstract class HttpMatcher<T extends HttpMatcher<T>> extends
|
||||
AsciidoctorCodeBlockMatcher<HttpMatcher<T>> {
|
||||
|
||||
private int headerOffset = 3;
|
||||
|
||||
protected HttpMatcher() {
|
||||
super("http");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T header(String name, String value) {
|
||||
this.addLine(this.headerOffset++, name + ": " + value);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class HttpResponseMatcher extends HttpMatcher<HttpResponseMatcher> {
|
||||
|
||||
public HttpResponseMatcher(HttpStatus status) {
|
||||
this.content("HTTP/1.1 " + status.value() + " " + status.getReasonPhrase());
|
||||
this.content("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class HttpRequestMatcher extends HttpMatcher<HttpRequestMatcher> {
|
||||
|
||||
public HttpRequestMatcher(RequestMethod requestMethod, String uri) {
|
||||
this.content(requestMethod.name() + " " + uri + " HTTP/1.1");
|
||||
this.content("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class AsciidoctorTableMatcher extends AbstractSnippetContentMatcher {
|
||||
|
||||
private AsciidoctorTableMatcher(String title, String... columns) {
|
||||
if (StringUtils.hasText(title)) {
|
||||
this.addLine("." + title);
|
||||
}
|
||||
this.addLine("|===");
|
||||
String header = "|"
|
||||
+ StringUtils
|
||||
.collectionToDelimitedString(Arrays.asList(columns), "|");
|
||||
this.addLine(header);
|
||||
this.addLine("");
|
||||
this.addLine("|===");
|
||||
}
|
||||
|
||||
public AsciidoctorTableMatcher row(String... entries) {
|
||||
for (String entry : entries) {
|
||||
this.addLine(-1, "|" + entry);
|
||||
}
|
||||
this.addLine(-1, "");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SnippetMatcher extends BaseMatcher<File> {
|
||||
|
||||
private Matcher<String> expectedContents;
|
||||
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
if (snippetFileExists(item)) {
|
||||
if (this.expectedContents != null) {
|
||||
try {
|
||||
return this.expectedContents.matches(read((File) item));
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean snippetFileExists(Object item) {
|
||||
return item instanceof File && ((File) item).isFile();
|
||||
}
|
||||
|
||||
private String read(File snippetFile) throws IOException {
|
||||
return FileCopyUtils.copyToString(new FileReader(snippetFile));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatch(Object item, Description description) {
|
||||
if (!snippetFileExists(item)) {
|
||||
description.appendText("The file " + item + " does not exist");
|
||||
}
|
||||
else if (this.expectedContents != null) {
|
||||
try {
|
||||
this.expectedContents
|
||||
.describeMismatch(read((File) item), description);
|
||||
}
|
||||
catch (IOException e) {
|
||||
description.appendText("The contents of " + item
|
||||
+ " cound not be read");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
if (this.expectedContents != null) {
|
||||
this.expectedContents.describeTo(description);
|
||||
}
|
||||
else {
|
||||
description.appendText("Asciidoctor snippet");
|
||||
}
|
||||
}
|
||||
|
||||
public SnippetMatcher withContents(Matcher<String> matcher) {
|
||||
this.expectedContents = matcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[source,bash]
|
||||
.{{title}}
|
||||
----
|
||||
$ curl {{arguments}}
|
||||
----
|
||||
@@ -0,0 +1,9 @@
|
||||
[source,http]
|
||||
.{{title}}
|
||||
----
|
||||
{{method}} {{path}} HTTP/1.1
|
||||
{{#headers}}
|
||||
{{name}}: {{value}}
|
||||
{{/headers}}
|
||||
{{requestBody}}
|
||||
----
|
||||
@@ -0,0 +1,9 @@
|
||||
[source,http]
|
||||
.{{title}}
|
||||
----
|
||||
HTTP/1.1 {{statusCode}} {{statusReason}}
|
||||
{{#headers}}
|
||||
{{name}}: {{value}}
|
||||
{{/headers}}
|
||||
{{responseBody}}
|
||||
----
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Relation|Description|Foo
|
||||
|
||||
{{#links}}
|
||||
|{{rel}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/links}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Relation|Description
|
||||
|
||||
{{#links}}
|
||||
|{{rel}}
|
||||
|{{description}}
|
||||
|
||||
{{/links}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Parameter|Description|Foo
|
||||
|
||||
{{#parameters}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/parameters}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Parameter|Description
|
||||
|
||||
{{#parameters}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|
||||
{{/parameters}}
|
||||
|===
|
||||
@@ -0,0 +1,11 @@
|
||||
|===
|
||||
|Path|Type|Description|Foo
|
||||
|
||||
{{#fields}}
|
||||
|{{path}}
|
||||
|{{type}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
@@ -0,0 +1,11 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Path|Type|Description
|
||||
|
||||
{{#fields}}
|
||||
|{{path}}
|
||||
|{{type}}
|
||||
|{{description}}
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
|===
|
||||
|Parameter|Description|Foo
|
||||
|
||||
{{#parameters}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/parameters}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Parameter|Description
|
||||
|
||||
{{#parameters}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|
||||
{{/parameters}}
|
||||
|===
|
||||
@@ -0,0 +1,11 @@
|
||||
|===
|
||||
|Path|Type|Description|Foo
|
||||
|
||||
{{#fields}}
|
||||
|{{path}}
|
||||
|{{type}}
|
||||
|{{description}}
|
||||
|{{foo}}
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
@@ -0,0 +1,11 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Path|Type|Description
|
||||
|
||||
{{#fields}}
|
||||
|{{path}}
|
||||
|{{type}}
|
||||
|{{description}}
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"_links": {
|
||||
"alpha": "http://alpha.example.com",
|
||||
"bravo": "http://bravo.example.com"
|
||||
},
|
||||
"_embedded": "embedded-test",
|
||||
"beta": "beta-value",
|
||||
"charlie": "charlie-value"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"_embedded": "embedded-test",
|
||||
"beta": "beta-value",
|
||||
"charlie": "charlie-value"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"_links": {
|
||||
"alpha": "http://alpha.example.com",
|
||||
"bravo": "http://bravo.example.com"
|
||||
},
|
||||
"beta": "beta-value",
|
||||
"charlie": "charlie-value"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"alpha": "alpha-value",
|
||||
"bravo": 123,
|
||||
"charlie": {
|
||||
"one": 456,
|
||||
"two": "two-value"
|
||||
},
|
||||
"delta": [
|
||||
"delta-value-1",
|
||||
"delta-value-2"
|
||||
],
|
||||
"echo": [{
|
||||
"one": 789,
|
||||
"two": "two-value"
|
||||
},{
|
||||
"one": 987,
|
||||
"two": "value-two"
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ }
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"alpha": "alpha-value"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"links": [ {
|
||||
"rel": "alpha",
|
||||
"href": "http://alpha.example.com"
|
||||
}, {
|
||||
"rel": "bravo",
|
||||
"href": "http://bravo.example.com"
|
||||
} ]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"links": [ {
|
||||
"rel": "alpha",
|
||||
"href": "http://alpha.example.com/one"
|
||||
}, {
|
||||
"rel": "alpha",
|
||||
"href": "http://alpha.example.com/two"
|
||||
} ]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ }
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"links": [ {
|
||||
"rel": "alpha",
|
||||
"href": "http://alpha.example.com"
|
||||
} ]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"_links": {
|
||||
"alpha": ["http://alpha.example.com/one", "http://alpha.example.com/two"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"_links": {
|
||||
"alpha": "http://alpha.example.com",
|
||||
"bravo": "http://bravo.example.com"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"_links": {
|
||||
"alpha": ["http://alpha.example.com/one", "http://alpha.example.com/two"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ }
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"_links": {
|
||||
"alpha": "http://alpha.example.com"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"_links": [ {
|
||||
"rel": "alpha",
|
||||
"href": "http://alpha.example.com/one"
|
||||
}, {
|
||||
"rel": "alpha",
|
||||
"href": "http://alpha.example.com/two"
|
||||
} ]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
javax.validation.constraints.NotNull.description=Should not be null
|
||||
Reference in New Issue
Block a user