Merge branch 'feature/eureka-stubs' into 2.0.x
This commit is contained in:
@@ -96,11 +96,28 @@
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!-- Only needed at compile time -->
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-restassured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-wiremock</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
@@ -116,7 +133,7 @@
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Serves *only* to filter the wro.xml so it can get an absolute
|
||||
<!-- Serves *only* to filter the wro.xml so it can get an absolute
|
||||
path for the project -->
|
||||
<id>copy-resources</id>
|
||||
<phase>validate</phase>
|
||||
@@ -133,6 +150,25 @@
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-docs</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>
|
||||
${project.build.outputDirectory}/static/docs
|
||||
</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>
|
||||
${project.build.directory}/generated-docs
|
||||
</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -168,6 +204,23 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-docs</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<backend>html</backend>
|
||||
<doctype>book</doctype>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.netflix.eureka.server.doc;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.jayway.restassured.RestAssured;
|
||||
import com.jayway.restassured.builder.RequestSpecBuilder;
|
||||
import com.jayway.restassured.filter.Filter;
|
||||
import com.jayway.restassured.specification.RequestSpecification;
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.embedded.LocalServerPort;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.contract.wiremock.restdocs.WireMockSnippet;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
|
||||
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
|
||||
import org.springframework.cloud.netflix.eureka.server.doc.AbstractDocumentationTests.Application;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.restdocs.JUnitRestDocumentation;
|
||||
import org.springframework.restdocs.restassured.RestAssuredRestDocumentation;
|
||||
import org.springframework.restdocs.restassured.RestDocumentationFilter;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
|
||||
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration;
|
||||
import static org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.modifyUris;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
"spring.jmx.enabled=true", "management.security.enabled=false" })
|
||||
@DirtiesContext
|
||||
public abstract class AbstractDocumentationTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Autowired
|
||||
private PeerAwareInstanceRegistryImpl registry;
|
||||
|
||||
@Autowired
|
||||
private EurekaInstanceConfigBean instanceConfig;
|
||||
|
||||
@Autowired
|
||||
private ApplicationInfoManager applicationInfoManager;
|
||||
|
||||
@Rule
|
||||
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(
|
||||
"target/generated-snippets");
|
||||
|
||||
@After
|
||||
public void init() {
|
||||
registry.clearRegistry();
|
||||
ReflectionTestUtils.setField(registry, "responseCache", null);
|
||||
registry.initializedResponseCache();
|
||||
}
|
||||
|
||||
protected InstanceInfo register(String name) {
|
||||
return register(name, UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
protected InstanceInfo register(String name, String id) {
|
||||
registry.register(instance(name, id), false);
|
||||
return instance();
|
||||
}
|
||||
|
||||
protected InstanceInfo instance(String name) {
|
||||
return instance(name, UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
protected InstanceInfo instance(String name, String id) {
|
||||
instanceConfig.setAppname(name);
|
||||
instanceConfig.setInstanceId(id);
|
||||
instanceConfig.setHostname("foo.example.com");
|
||||
applicationInfoManager.initComponent(instanceConfig);
|
||||
return applicationInfoManager.getInfo();
|
||||
}
|
||||
|
||||
protected InstanceInfo instance() {
|
||||
return applicationInfoManager.getInfo();
|
||||
}
|
||||
|
||||
private RestDocumentationFilter filter(String name) {
|
||||
return RestAssuredRestDocumentation.document(name,
|
||||
preprocessRequest(modifyUris().host("eureka.example.com").removePort(),
|
||||
prettyPrint()),
|
||||
preprocessResponse(prettyPrint()));
|
||||
}
|
||||
|
||||
private RequestSpecification spec(Filter... filters) {
|
||||
return spec(null, filters);
|
||||
}
|
||||
|
||||
private RequestSpecification spec(Object body, Filter... filters) {
|
||||
RequestSpecBuilder builder = new RequestSpecBuilder()
|
||||
.addFilter(documentationConfiguration(this.restDocumentation).snippets()
|
||||
.withAdditionalDefaults(new WireMockSnippet()));
|
||||
for (Filter filter : filters) {
|
||||
builder = builder.addFilter(filter);
|
||||
}
|
||||
RequestSpecification spec = builder.setPort(this.port).build();
|
||||
if (body != null) {
|
||||
spec.contentType("application/json").body(body, new EurekaObjectMapper());
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
protected RequestSpecification document() {
|
||||
return document("{method-name}");
|
||||
}
|
||||
|
||||
protected RequestSpecification document(Object body) {
|
||||
RestDocumentationFilter filter = filter("{method-name}");
|
||||
RequestSpecification assured = RestAssured.given(spec(body, filter));
|
||||
return assured.filter(filter);
|
||||
}
|
||||
|
||||
protected RequestSpecification document(String name, Object body) {
|
||||
RestDocumentationFilter filter = filter(name);
|
||||
RequestSpecification assured = RestAssured.given(spec(body, filter));
|
||||
return assured.filter(filter);
|
||||
}
|
||||
|
||||
protected RequestSpecification document(String name) {
|
||||
RestDocumentationFilter filter = filter(name);
|
||||
return RestAssured.given(spec(filter)).filter(filter);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableEurekaServer
|
||||
protected static class Application {
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(Application.class).properties(
|
||||
"spring.application.name=eureka", "management.security.enabled=false",
|
||||
"eureka.client.registerWithEureka=true").run(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.netflix.eureka.server.doc;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.delete;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.matching;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.put;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.emptyIterable;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.springframework.cloud.netflix.eureka.server.doc.RequestVerifierFilter.verify;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class AppRegistrationTests extends AbstractDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void startingApp() throws Exception {
|
||||
register("foo");
|
||||
document().accept("application/json").when().get("/eureka/apps").then()
|
||||
.assertThat()
|
||||
.body("applications.application", hasSize(1),
|
||||
"applications.application[0].instance[0].status",
|
||||
equalTo("STARTING"))
|
||||
.statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addInstance() throws Exception {
|
||||
document(instance("foo"))
|
||||
.filter(verify("$.instance.app").json("$.instance.hostName")
|
||||
.json("$.instance[?(@.status=='STARTING')]")
|
||||
.json("$.instance.instanceId")
|
||||
.json("$.instance.dataCenterInfo.name"))
|
||||
.when().post("/eureka/apps/FOO").then().assertThat().statusCode(is(204));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setStatus() throws Exception {
|
||||
String id = register("foo").getInstanceId();
|
||||
document()
|
||||
.filter(verify(put(urlPathMatching("/eureka/apps/FOO/.*/status"))
|
||||
.withQueryParam("value", matching("UP"))))
|
||||
.when().put("/eureka/apps/FOO/{id}/status?value={value}", id, "UP").then()
|
||||
.assertThat().statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allApps() throws Exception {
|
||||
register("foo");
|
||||
document().accept("application/json").when().get("/eureka/apps").then()
|
||||
.assertThat().body("applications.application", hasSize(1))
|
||||
.statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delta() throws Exception {
|
||||
register("foo");
|
||||
document().accept("application/json").when().get("/eureka/apps/delta").then()
|
||||
.assertThat().body("applications.application", hasSize(1))
|
||||
.statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneInstance() throws Exception {
|
||||
String id = UUID.randomUUID().toString();
|
||||
register("foo", id);
|
||||
document().filter(verify(get(urlPathMatching("/eureka/apps/FOO/.*"))))
|
||||
.accept("application/json").when().get("/eureka/apps/FOO/{id}", id).then()
|
||||
.assertThat().body("instance.app", equalTo("FOO")).statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupInstance() throws Exception {
|
||||
String id = register("foo").getInstanceId();
|
||||
document().filter(verify(get(urlPathMatching("/eureka/instances/.*"))))
|
||||
.accept("application/json").when().get("/eureka/instances/{id}", id)
|
||||
.then().assertThat().body("instance.app", equalTo("FOO"))
|
||||
.statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renew() throws Exception {
|
||||
String id = register("foo").getInstanceId();
|
||||
document().filter(verify(put(urlPathMatching("/eureka/apps/FOO/.*"))))
|
||||
.accept("application/json").when().put("/eureka/apps/FOO/{id}", id).then()
|
||||
.assertThat().statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateMetadata() throws Exception {
|
||||
String id = register("foo").getInstanceId();
|
||||
document()
|
||||
.filter(verify(put(urlPathMatching("/eureka/apps/FOO/.*/metadata"))
|
||||
.withQueryParam("key", matching(".*"))))
|
||||
.accept("application/json").when()
|
||||
.put("/eureka/apps/FOO/{id}/metadata?key=value", id).then().assertThat()
|
||||
.statusCode(is(200));
|
||||
assertThat(instance().getMetadata()).containsEntry("key", "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteInstance() throws Exception {
|
||||
String id = register("foo").getInstanceId();
|
||||
document().filter(verify(delete(urlPathMatching("/eureka/apps/FOO/.*")))).when()
|
||||
.delete("/eureka/apps/FOO/{id}", id).then().assertThat()
|
||||
.statusCode(is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyApps() {
|
||||
document().when().accept("application/json").get("/eureka/apps").then()
|
||||
.assertThat().body("applications.application", emptyIterable())
|
||||
.statusCode(is(200));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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.cloud.netflix.eureka.server.doc;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import com.jayway.restassured.mapper.ObjectMapperDeserializationContext;
|
||||
import com.jayway.restassured.mapper.ObjectMapperSerializationContext;
|
||||
import com.netflix.discovery.converters.EntityBodyConverter;
|
||||
|
||||
final class EurekaObjectMapper
|
||||
implements com.jayway.restassured.mapper.ObjectMapper {
|
||||
private EntityBodyConverter converter = new EntityBodyConverter();
|
||||
|
||||
@Override
|
||||
public Object serialize(ObjectMapperSerializationContext context) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
converter.write(context.getObjectToSerialize(), out,
|
||||
MediaType.APPLICATION_JSON_TYPE);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot serialize", e);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(
|
||||
ObjectMapperDeserializationContext context) {
|
||||
try {
|
||||
return converter.read(
|
||||
context.getDataToDeserialize().asInputStream(),
|
||||
context.getType(), MediaType.APPLICATION_JSON_TYPE);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot deserialize", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.netflix.eureka.server.doc;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
// TODO: maybe this should be the default (the test fails without it because the JSON is
|
||||
// invalid)
|
||||
@TestPropertySource(properties = "eureka.server.minAvailableInstancesForPeerReplication=0")
|
||||
public class EurekaServerTests extends AbstractDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void serverStatus() throws Exception {
|
||||
register("foo", UUID.randomUUID().toString());
|
||||
document().accept("application/json").when().get("/eureka/status").then()
|
||||
.assertThat().body("generalStats", notNullValue(), "applicationStats",
|
||||
notNullValue(), "instanceInfo", notNullValue())
|
||||
.statusCode(is(200));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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.cloud.netflix.eureka.server.doc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.MappingBuilder;
|
||||
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
|
||||
import com.github.tomakehurst.wiremock.http.ContentTypeHeader;
|
||||
import com.github.tomakehurst.wiremock.http.Cookie;
|
||||
import com.github.tomakehurst.wiremock.http.HttpHeader;
|
||||
import com.github.tomakehurst.wiremock.http.HttpHeaders;
|
||||
import com.github.tomakehurst.wiremock.http.QueryParameter;
|
||||
import com.github.tomakehurst.wiremock.http.Request;
|
||||
import com.github.tomakehurst.wiremock.http.RequestMethod;
|
||||
import com.github.tomakehurst.wiremock.matching.MatchResult;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.restassured.filter.Filter;
|
||||
import com.jayway.restassured.filter.FilterContext;
|
||||
import com.jayway.restassured.response.Header;
|
||||
import com.jayway.restassured.response.Response;
|
||||
import com.jayway.restassured.specification.FilterableRequestSpecification;
|
||||
import com.jayway.restassured.specification.FilterableResponseSpecification;
|
||||
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class RequestVerifierFilter implements Filter {
|
||||
|
||||
static final String CONTEXT_KEY_CONFIGURATION = "org.springframework.restdocs.configuration";
|
||||
private Map<String, JsonPath> jsonPaths = new LinkedHashMap<>();
|
||||
private MappingBuilder builder;
|
||||
|
||||
public static RequestVerifierFilter verify(String path) {
|
||||
return new RequestVerifierFilter(path);
|
||||
}
|
||||
|
||||
public static RequestVerifierFilter verify(MappingBuilder builder) {
|
||||
return new RequestVerifierFilter().wiremock(builder);
|
||||
}
|
||||
|
||||
private RequestVerifierFilter(String expression, Object... args) {
|
||||
expression = String.format(expression, args);
|
||||
this.jsonPaths.put(expression, JsonPath.compile(expression));
|
||||
}
|
||||
|
||||
private RequestVerifierFilter() {
|
||||
}
|
||||
|
||||
public RequestVerifierFilter json(String expression, Object... args) {
|
||||
expression = String.format(expression, args);
|
||||
this.jsonPaths.put(expression, JsonPath.compile(expression));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequestVerifierFilter wiremock(MappingBuilder builder) {
|
||||
this.builder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response filter(FilterableRequestSpecification requestSpec,
|
||||
FilterableResponseSpecification responseSpec, FilterContext context) {
|
||||
Map<String, Object> configuration = getConfiguration(requestSpec, context);
|
||||
configuration.put("contract.jsonPaths", this.jsonPaths.keySet());
|
||||
Response response = context.next(requestSpec, responseSpec);
|
||||
if (requestSpec.getBody() != null && !this.jsonPaths.isEmpty()) {
|
||||
String actual = new String((byte[]) requestSpec.getBody());
|
||||
for (JsonPath jsonPath : this.jsonPaths.values()) {
|
||||
new JsonPathValue(jsonPath, actual).assertHasValue(Object.class,
|
||||
"an object");
|
||||
}
|
||||
}
|
||||
if (this.builder != null) {
|
||||
this.builder.willReturn(getResponseDefinition(response));
|
||||
StubMapping stubMapping = this.builder.build();
|
||||
MatchResult match = stubMapping.getRequest()
|
||||
.match(new WireMockRestAssuredRequestAdapter(requestSpec));
|
||||
assertThat(match.isExactMatch()).as("wiremock did not match request")
|
||||
.isTrue();
|
||||
configuration.put("contract.stubMapping", stubMapping);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private ResponseDefinitionBuilder getResponseDefinition(Response response) {
|
||||
ResponseDefinitionBuilder definition = ResponseDefinitionBuilder
|
||||
.responseDefinition().withBody(response.getBody().asString())
|
||||
.withStatus(response.getStatusCode());
|
||||
addResponseHeaders(definition, response);
|
||||
return definition;
|
||||
}
|
||||
|
||||
private void addResponseHeaders(ResponseDefinitionBuilder definition,
|
||||
Response input) {
|
||||
for (Header header : input.getHeaders().asList()) {
|
||||
String name = header.getName();
|
||||
definition.withHeader(name, input.getHeader(name));
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> getConfiguration(
|
||||
FilterableRequestSpecification requestSpec, FilterContext context) {
|
||||
Map<String, Object> configuration = context
|
||||
.<Map<String, Object>>getValue(CONTEXT_KEY_CONFIGURATION);
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
class JsonPathValue {
|
||||
|
||||
private final JsonPath jsonPath;
|
||||
private final String expression;
|
||||
private final CharSequence actual;
|
||||
|
||||
JsonPathValue(JsonPath jsonPath, CharSequence actual) {
|
||||
this.jsonPath = jsonPath;
|
||||
this.actual = actual;
|
||||
this.expression = jsonPath.getPath();
|
||||
}
|
||||
|
||||
public void assertHasValue(Class<?> type, String expectedDescription) {
|
||||
Object value = getValue(true);
|
||||
if (value == null || isIndefiniteAndEmpty()) {
|
||||
throw new AssertionError(getNoValueMessage());
|
||||
}
|
||||
if (type != null && !type.isInstance(value)) {
|
||||
throw new AssertionError(getExpectedValueMessage(expectedDescription));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIndefiniteAndEmpty() {
|
||||
return !isDefinite() && isEmpty();
|
||||
}
|
||||
|
||||
private boolean isDefinite() {
|
||||
return this.jsonPath.isDefinite();
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return ObjectUtils.isEmpty(getValue(false));
|
||||
}
|
||||
|
||||
public Object getValue(boolean required) {
|
||||
try {
|
||||
CharSequence json = this.actual;
|
||||
return this.jsonPath.read(json == null ? null : json.toString());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (!required) {
|
||||
return null;
|
||||
}
|
||||
throw new AssertionError(getNoValueMessage() + ". " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getNoValueMessage() {
|
||||
return "No value at JSON path \"" + this.expression + "\"";
|
||||
}
|
||||
|
||||
private String getExpectedValueMessage(String expectedDescription) {
|
||||
return String.format("Expected %s at JSON path \"%s\" but found: %s",
|
||||
expectedDescription, this.expression,
|
||||
ObjectUtils.nullSafeToString(StringUtils.quoteIfString(getValue(false))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WireMockRestAssuredRequestAdapter implements Request {
|
||||
|
||||
private FilterableRequestSpecification request;
|
||||
|
||||
public WireMockRestAssuredRequestAdapter(FilterableRequestSpecification request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return request.getDerivedPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAbsoluteUrl() {
|
||||
return request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestMethod getMethod() {
|
||||
return RequestMethod.fromString(request.getMethod().name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientIp() {
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String key) {
|
||||
String value = request.getHeaders().getValue(key);
|
||||
if ("accept".equals(key.toLowerCase()) && "*/*".equals(value)) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeader header(String key) {
|
||||
String value = request.getHeaders().getValue(key);
|
||||
if ("accept".equals(key.toLowerCase()) && "*/*".equals(value)) {
|
||||
return null;
|
||||
}
|
||||
return new HttpHeader(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentTypeHeader contentTypeHeader() {
|
||||
return new ContentTypeHeader(request.getContentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
List<HttpHeader> headers = new ArrayList<>();
|
||||
for (Header header : request.getHeaders()) {
|
||||
String value = header.getValue();
|
||||
if ("accept".equals(header.getName().toLowerCase()) && "*/*".equals(value)) {
|
||||
continue;
|
||||
}
|
||||
headers.add(new HttpHeader(header.getName(), header.getValue()));
|
||||
}
|
||||
return new HttpHeaders(headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsHeader(String key) {
|
||||
String value = request.getHeaders().getValue(key);
|
||||
if ("accept".equals(key.toLowerCase()) && "*/*".equals(value)) {
|
||||
return false;
|
||||
}
|
||||
return request.getHeaders().hasHeaderWithName(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAllHeaderKeys() {
|
||||
Set<String> headers = new LinkedHashSet<>();
|
||||
for (Header header : request.getHeaders()) {
|
||||
String value = header.getValue();
|
||||
if ("accept".equals(header.getName().toLowerCase()) && "*/*".equals(value)) {
|
||||
continue;
|
||||
}
|
||||
headers.add(header.getName());
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Cookie> getCookies() {
|
||||
Map<String, Cookie> map = new LinkedHashMap<>();
|
||||
for (com.jayway.restassured.response.Cookie cookie : request.getCookies()) {
|
||||
Cookie value = new Cookie(cookie.getValue());
|
||||
map.put(cookie.getName(), value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameter queryParameter(String key) {
|
||||
Map<String, String> params = request.getQueryParams();
|
||||
if (params.containsKey(key)) {
|
||||
return new QueryParameter(key, Arrays.asList(params.get(key)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBody() {
|
||||
return request.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBodyAsString() {
|
||||
return new String(getBody());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBodyAsBase64() {
|
||||
return Base64Utils.encodeToString(getBody());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBrowserProxyRequest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
server.port=8761
|
||||
spring.application.name=eureka
|
||||
eureka.client.registerWithEureka=false
|
||||
eureka.client.fetchRegistry=false
|
||||
eureka.client.fetchRegistry=false
|
||||
logging.level.org.springframework.web.client=DEBUG
|
||||
logging.level.com.netflix.discovery=DEBUG
|
||||
Reference in New Issue
Block a user