Browse Source
* Copy of feign-mock from git@github.com:velo/feign-mock.git * Fix license headers, remove .tool files and make code java 6 compatible * Remove all badges * Update feign GitHub link * Remove link to orignal projectpull/698/head
Marvin Froeder
7 years ago
committed by
GitHub
13 changed files with 1883 additions and 0 deletions
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
# feign-mock |
||||
|
||||
An easy way to test https://github.com/OpenFeign/feign. Since feign stores most of the logic in annotations, this helps to check if the annotations are correct. |
||||
|
||||
The original article is available https://velo.github.io/2016/06/05/Testing-feign-clients.html[here] |
||||
|
||||
If mocking feign clients is easy, testing the logic written in annotations is not! |
||||
|
||||
To check if you are parsing the request/response properly, the only way is firing a real request. Well, that doesn't seem to be a good path to unit (or even integration) test remote services. Any IO change will affect test stability. |
||||
|
||||
With feign-mock you can use pre-loaded JSON strings or streams as content for your responses. It also allows you to verify mocked invocations and feign-mock will hit your annotations to make sure everything works. |
||||
|
||||
##### Example |
||||
|
||||
``` |
||||
private GitHub github; |
||||
private MockClient mockClient; |
||||
|
||||
@Before |
||||
public void setup() throws IOException { |
||||
mockClient = new MockClient() |
||||
.noContent(HttpMethod.PATCH, "/repos/velo/feign-mock/contributors"); |
||||
|
||||
github = Feign.builder() |
||||
.decoder(new GsonDecoder()) |
||||
.client(mockClient) |
||||
.target(new MockTarget<>(GitHub.class)); |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
mockClient.verifyStatus(); |
||||
} |
||||
|
||||
@Test |
||||
public void missHttpMethod() { |
||||
List<Contributor> result = github.patchContributors("velo", "feign-mock"); |
||||
assertThat(result, nullValue()); |
||||
mockClient.verifyOne(HttpMethod.PATCH, "/repos/velo/feign-mock/contributors"); |
||||
} |
||||
``` |
||||
|
||||
This simple test returns no content and verifies that the URL was truly invoked. |
||||
|
||||
On the mocked client, you can include all URLs and methods you want to mock. |
||||
|
||||
For more comprehensive examples take a look at https://github.com/OpenFeign/feign/blob/master/mock/src/test/java/feign/mock/MockClientTest.java[MockClientTest]. |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- |
||||
|
||||
Copyright 2012-2018 The Feign 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. |
||||
|
||||
--> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<parent> |
||||
<groupId>io.github.openfeign</groupId> |
||||
<artifactId>parent</artifactId> |
||||
<version>9.7.0-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<artifactId>feign-mock</artifactId> |
||||
<name>Feign Mock</name> |
||||
<description>Feign Mock</description> |
||||
|
||||
<properties> |
||||
<main.basedir>${project.basedir}/..</main.basedir> |
||||
|
||||
<hamcrest.version>1.3</hamcrest.version> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>feign-core</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>feign-gson</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.hamcrest</groupId> |
||||
<artifactId>hamcrest-core</artifactId> |
||||
<version>${hamcrest.version}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.hamcrest</groupId> |
||||
<artifactId>hamcrest-library</artifactId> |
||||
<version>${hamcrest.version}</version> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
</project> |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
public enum HttpMethod { |
||||
|
||||
GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH |
||||
|
||||
} |
@ -0,0 +1,276 @@
@@ -0,0 +1,276 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
import static feign.Util.UTF_8; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.net.HttpURLConnection; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import feign.Client; |
||||
import feign.Request; |
||||
import feign.Response; |
||||
import feign.Util; |
||||
|
||||
public class MockClient implements Client { |
||||
|
||||
class RequestResponse { |
||||
|
||||
private final RequestKey requestKey; |
||||
|
||||
private final Response.Builder responseBuilder; |
||||
|
||||
public RequestResponse(RequestKey requestKey, Response.Builder responseBuilder) { |
||||
this.requestKey = requestKey; |
||||
this.responseBuilder = responseBuilder; |
||||
} |
||||
|
||||
} |
||||
|
||||
public static final Map<String, Collection<String>> EMPTY_HEADERS = Collections.emptyMap(); |
||||
|
||||
private final List<RequestResponse> responses = new ArrayList<RequestResponse>(); |
||||
|
||||
private final Map<RequestKey, List<Request>> requests = new HashMap<RequestKey, List<Request>>(); |
||||
|
||||
private boolean sequential; |
||||
|
||||
private Iterator<RequestResponse> responseIterator; |
||||
|
||||
public MockClient() { |
||||
} |
||||
|
||||
public MockClient(boolean sequential) { |
||||
this.sequential = sequential; |
||||
} |
||||
|
||||
@Override |
||||
public synchronized Response execute(Request request, Request.Options options) throws IOException { |
||||
RequestKey requestKey = RequestKey.create(request); |
||||
Response.Builder responseBuilder; |
||||
if (sequential) { |
||||
responseBuilder = executeSequential(requestKey); |
||||
} else { |
||||
responseBuilder = executeAny(request, requestKey); |
||||
} |
||||
|
||||
return responseBuilder.request(request).build(); |
||||
} |
||||
|
||||
private Response.Builder executeSequential(RequestKey requestKey) { |
||||
Response.Builder responseBuilder; |
||||
if (responseIterator == null) { |
||||
responseIterator = responses.iterator(); |
||||
} |
||||
if (!responseIterator.hasNext()) { |
||||
throw new VerificationAssertionError("Received excessive request %s", requestKey); |
||||
} |
||||
|
||||
RequestResponse expectedRequestResponse = responseIterator.next(); |
||||
if (!expectedRequestResponse.requestKey.equalsExtended(requestKey)) { |
||||
throw new VerificationAssertionError("Expected %s, but was %s", expectedRequestResponse.requestKey, |
||||
requestKey); |
||||
} |
||||
|
||||
responseBuilder = expectedRequestResponse.responseBuilder; |
||||
return responseBuilder; |
||||
} |
||||
|
||||
private Response.Builder executeAny(Request request, RequestKey requestKey) { |
||||
Response.Builder responseBuilder; |
||||
if (requests.containsKey(requestKey)) { |
||||
requests.get(requestKey).add(request); |
||||
} else { |
||||
requests.put(requestKey, new ArrayList<Request>(Arrays.asList(request))); |
||||
} |
||||
|
||||
responseBuilder = getResponseBuilder(request, requestKey); |
||||
return responseBuilder; |
||||
} |
||||
|
||||
private Response.Builder getResponseBuilder(Request request, RequestKey requestKey) { |
||||
Response.Builder responseBuilder = null; |
||||
for (RequestResponse requestResponse : responses) { |
||||
if (requestResponse.requestKey.equalsExtended(requestKey)) { |
||||
responseBuilder = requestResponse.responseBuilder; |
||||
// Don't break here, last one should win to be compatible with
|
||||
// previous
|
||||
// releases of this library!
|
||||
} |
||||
} |
||||
if (responseBuilder == null) { |
||||
responseBuilder = Response.builder().status(HttpURLConnection.HTTP_NOT_FOUND).reason("Not mocker") |
||||
.headers(request.headers()); |
||||
} |
||||
return responseBuilder; |
||||
} |
||||
|
||||
public MockClient ok(HttpMethod method, String url, InputStream responseBody) throws IOException { |
||||
return ok(RequestKey.builder(method, url).build(), responseBody); |
||||
} |
||||
|
||||
public MockClient ok(HttpMethod method, String url, String responseBody) { |
||||
return ok(RequestKey.builder(method, url).build(), responseBody); |
||||
} |
||||
|
||||
public MockClient ok(HttpMethod method, String url, byte[] responseBody) { |
||||
return ok(RequestKey.builder(method, url).build(), responseBody); |
||||
} |
||||
|
||||
public MockClient ok(HttpMethod method, String url) { |
||||
return ok(RequestKey.builder(method, url).build()); |
||||
} |
||||
|
||||
public MockClient ok(RequestKey requestKey, InputStream responseBody) throws IOException { |
||||
return ok(requestKey, Util.toByteArray(responseBody)); |
||||
} |
||||
|
||||
public MockClient ok(RequestKey requestKey, String responseBody) { |
||||
return ok(requestKey, responseBody.getBytes(UTF_8)); |
||||
} |
||||
|
||||
public MockClient ok(RequestKey requestKey, byte[] responseBody) { |
||||
return add(requestKey, HttpURLConnection.HTTP_OK, responseBody); |
||||
} |
||||
|
||||
public MockClient ok(RequestKey requestKey) { |
||||
return ok(requestKey, (byte[]) null); |
||||
} |
||||
|
||||
public MockClient add(HttpMethod method, String url, int status, InputStream responseBody) throws IOException { |
||||
return add(RequestKey.builder(method, url).build(), status, responseBody); |
||||
} |
||||
|
||||
public MockClient add(HttpMethod method, String url, int status, String responseBody) { |
||||
return add(RequestKey.builder(method, url).build(), status, responseBody); |
||||
} |
||||
|
||||
public MockClient add(HttpMethod method, String url, int status, byte[] responseBody) { |
||||
return add(RequestKey.builder(method, url).build(), status, responseBody); |
||||
} |
||||
|
||||
public MockClient add(HttpMethod method, String url, int status) { |
||||
return add(RequestKey.builder(method, url).build(), status); |
||||
} |
||||
|
||||
/** |
||||
* @param response |
||||
* <ul> |
||||
* <li>the status defaults to 0, not 200!</li> |
||||
* <li>the internal feign-code requires the headers to be |
||||
* set</li> |
||||
* </ul> |
||||
*/ |
||||
public MockClient add(HttpMethod method, String url, Response.Builder response) { |
||||
return add(RequestKey.builder(method, url).build(), response); |
||||
} |
||||
|
||||
public MockClient add(RequestKey requestKey, int status, InputStream responseBody) throws IOException { |
||||
return add(requestKey, status, Util.toByteArray(responseBody)); |
||||
} |
||||
|
||||
public MockClient add(RequestKey requestKey, int status, String responseBody) { |
||||
return add(requestKey, status, responseBody.getBytes(UTF_8)); |
||||
} |
||||
|
||||
public MockClient add(RequestKey requestKey, int status, byte[] responseBody) { |
||||
return add(requestKey, |
||||
Response.builder().status(status).reason("Mocked").headers(EMPTY_HEADERS).body(responseBody)); |
||||
} |
||||
|
||||
public MockClient add(RequestKey requestKey, int status) { |
||||
return add(requestKey, status, (byte[]) null); |
||||
} |
||||
|
||||
public MockClient add(RequestKey requestKey, Response.Builder response) { |
||||
responses.add(new RequestResponse(requestKey, response)); |
||||
return this; |
||||
} |
||||
|
||||
public MockClient add(HttpMethod method, String url, Response response) { |
||||
return this.add(method, url, response.toBuilder()); |
||||
} |
||||
|
||||
public MockClient noContent(HttpMethod method, String url) { |
||||
return add(method, url, HttpURLConnection.HTTP_NO_CONTENT); |
||||
} |
||||
|
||||
public Request verifyOne(HttpMethod method, String url) { |
||||
return verifyTimes(method, url, 1).get(0); |
||||
} |
||||
|
||||
public List<Request> verifyTimes(final HttpMethod method, final String url, final int times) { |
||||
if (times < 0) { |
||||
throw new IllegalArgumentException("times must be a non negative number"); |
||||
} |
||||
|
||||
if (times == 0) { |
||||
verifyNever(method, url); |
||||
return Collections.emptyList(); |
||||
} |
||||
|
||||
RequestKey requestKey = RequestKey.builder(method, url).build(); |
||||
if (!requests.containsKey(requestKey)) { |
||||
throw new VerificationAssertionError("Wanted: '%s' but never invoked!", requestKey); |
||||
} |
||||
|
||||
List<Request> result = requests.get(requestKey); |
||||
if (result.size() != times) { |
||||
throw new VerificationAssertionError("Wanted: '%s' to be invoked: '%s' times but got: '%s'!", requestKey, |
||||
times, result.size()); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
public void verifyNever(HttpMethod method, String url) { |
||||
RequestKey requestKey = RequestKey.builder(method, url).build(); |
||||
if (requests.containsKey(requestKey)) { |
||||
throw new VerificationAssertionError("Do not wanted: '%s' but was invoked!", requestKey); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* To be called in an @After method: |
||||
* |
||||
* <pre> |
||||
* @After |
||||
* public void tearDown() { |
||||
* mockClient.verifyStatus(); |
||||
* } |
||||
* </pre> |
||||
*/ |
||||
public void verifyStatus() { |
||||
if (sequential) { |
||||
boolean unopenedIterator = responseIterator == null && !responses.isEmpty(); |
||||
if (unopenedIterator || responseIterator.hasNext()) { |
||||
throw new VerificationAssertionError("More executions were expected"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void resetRequests() { |
||||
requests.clear(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
import feign.Request; |
||||
import feign.RequestTemplate; |
||||
import feign.Target; |
||||
|
||||
public class MockTarget<E> implements Target<E> { |
||||
|
||||
private final Class<E> type; |
||||
|
||||
public MockTarget(Class<E> type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
@Override |
||||
public Class<E> type() { |
||||
return type; |
||||
} |
||||
|
||||
@Override |
||||
public String name() { |
||||
return type.getSimpleName(); |
||||
} |
||||
|
||||
@Override |
||||
public String url() { |
||||
return ""; |
||||
} |
||||
|
||||
@Override |
||||
public Request apply(RequestTemplate input) { |
||||
input.insert(0, url()); |
||||
return input.request(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,173 @@
@@ -0,0 +1,173 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
import static feign.Util.UTF_8; |
||||
|
||||
import java.io.UnsupportedEncodingException; |
||||
import java.net.URLDecoder; |
||||
import java.nio.charset.Charset; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.Map; |
||||
import feign.Request; |
||||
import feign.Util; |
||||
|
||||
public class RequestKey { |
||||
|
||||
public static class Builder { |
||||
|
||||
private final HttpMethod method; |
||||
|
||||
private final String url; |
||||
|
||||
private Map<String, Collection<String>> headers; |
||||
|
||||
private Charset charset; |
||||
|
||||
private byte[] body; |
||||
|
||||
private Builder(HttpMethod method, String url) { |
||||
this.method = method; |
||||
this.url = url; |
||||
} |
||||
|
||||
public Builder headers(Map<String, Collection<String>> headers) { |
||||
this.headers = headers; |
||||
return this; |
||||
} |
||||
|
||||
public Builder charset(Charset charset) { |
||||
this.charset = charset; |
||||
return this; |
||||
} |
||||
|
||||
public Builder body(String body) { |
||||
return body(body.getBytes(UTF_8)); |
||||
} |
||||
|
||||
public Builder body(byte[] body) { |
||||
this.body = body; |
||||
return this; |
||||
} |
||||
|
||||
public RequestKey build() { |
||||
return new RequestKey(this); |
||||
} |
||||
|
||||
} |
||||
|
||||
public static Builder builder(HttpMethod method, String url) { |
||||
return new Builder(method, url); |
||||
} |
||||
|
||||
public static RequestKey create(Request request) { |
||||
return new RequestKey(request); |
||||
} |
||||
|
||||
private static String buildUrl(Request request) { |
||||
try { |
||||
return URLDecoder.decode(request.url(), Util.UTF_8.name()); |
||||
} catch (final UnsupportedEncodingException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private final HttpMethod method; |
||||
|
||||
private final String url; |
||||
|
||||
private final Map<String, Collection<String>> headers; |
||||
|
||||
private final Charset charset; |
||||
|
||||
private final byte[] body; |
||||
|
||||
private RequestKey(Builder builder) { |
||||
this.method = builder.method; |
||||
this.url = builder.url; |
||||
this.headers = builder.headers; |
||||
this.charset = builder.charset; |
||||
this.body = builder.body; |
||||
} |
||||
|
||||
private RequestKey(Request request) { |
||||
this.method = HttpMethod.valueOf(request.method()); |
||||
this.url = buildUrl(request); |
||||
this.headers = request.headers(); |
||||
this.charset = request.charset(); |
||||
this.body = request.body(); |
||||
} |
||||
|
||||
public HttpMethod getMethod() { |
||||
return method; |
||||
} |
||||
|
||||
public String getUrl() { |
||||
return url; |
||||
} |
||||
|
||||
public Map<String, Collection<String>> getHeaders() { |
||||
return headers; |
||||
} |
||||
|
||||
public Charset getCharset() { |
||||
return charset; |
||||
} |
||||
|
||||
public byte[] getBody() { |
||||
return body; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = prime * result + ((method == null) ? 0 : method.hashCode()); |
||||
result = prime * result + ((url == null) ? 0 : url.hashCode()); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) return true; |
||||
if (obj == null) return false; |
||||
if (getClass() != obj.getClass()) return false; |
||||
final RequestKey other = (RequestKey) obj; |
||||
if (method != other.method) return false; |
||||
if (url == null) { |
||||
if (other.url != null) return false; |
||||
} else if (!url.equals(other.url)) return false; |
||||
return true; |
||||
} |
||||
|
||||
public boolean equalsExtended(Object obj) { |
||||
if (equals(obj)) { |
||||
RequestKey other = (RequestKey) obj; |
||||
boolean headersEqual = other.headers == null || headers == null || headers.equals(other.headers); |
||||
boolean charsetEqual = other.charset == null || charset == null || charset.equals(other.charset); |
||||
boolean bodyEqual = other.body == null || body == null || Arrays.equals(other.body, body); |
||||
return headersEqual && charsetEqual && bodyEqual; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return String.format("Request [%s %s: %s headers and %s]", method, url, |
||||
headers == null ? "without" : "with " + headers.size(), |
||||
charset == null ? "no charset" : "charset " + charset); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
public class VerificationAssertionError extends AssertionError { |
||||
|
||||
private static final long serialVersionUID = -3302777023656958993L; |
||||
|
||||
public VerificationAssertionError(String message, Object... arguments) { |
||||
super(String.format(message, arguments)); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
import static feign.Util.toByteArray; |
||||
import static org.hamcrest.MatcherAssert.assertThat; |
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.notNullValue; |
||||
import static org.hamcrest.Matchers.startsWith; |
||||
import static org.junit.Assert.fail; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.lang.reflect.Type; |
||||
import java.util.List; |
||||
|
||||
import javax.net.ssl.HttpsURLConnection; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import feign.Body; |
||||
import feign.Feign; |
||||
import feign.FeignException; |
||||
import feign.Param; |
||||
import feign.RequestLine; |
||||
import feign.Response; |
||||
import feign.codec.DecodeException; |
||||
import feign.codec.Decoder; |
||||
import feign.gson.GsonDecoder; |
||||
|
||||
public class MockClientSequentialTest { |
||||
|
||||
interface GitHub { |
||||
|
||||
@RequestLine("GET /repos/{owner}/{repo}/contributors") |
||||
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); |
||||
|
||||
@RequestLine("GET /repos/{owner}/{repo}/contributors?client_id={client_id}") |
||||
List<Contributor> contributors(@Param("client_id") String clientId, @Param("owner") String owner, |
||||
@Param("repo") String repo); |
||||
|
||||
@RequestLine("PATCH /repos/{owner}/{repo}/contributors") |
||||
List<Contributor> patchContributors(@Param("owner") String owner, @Param("repo") String repo); |
||||
|
||||
@RequestLine("POST /repos/{owner}/{repo}/contributors") |
||||
@Body("%7B\"login\":\"{login}\",\"type\":\"{type}\"%7D") |
||||
Contributor create(@Param("owner") String owner, @Param("repo") String repo, @Param("login") String login, |
||||
@Param("type") String type); |
||||
|
||||
} |
||||
|
||||
static class Contributor { |
||||
|
||||
String login; |
||||
|
||||
int contributions; |
||||
|
||||
} |
||||
|
||||
class AssertionDecoder implements Decoder { |
||||
|
||||
private final Decoder delegate; |
||||
|
||||
public AssertionDecoder(Decoder delegate) { |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
@Override |
||||
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { |
||||
assertThat(response.request(), notNullValue()); |
||||
|
||||
return delegate.decode(response, type); |
||||
} |
||||
|
||||
} |
||||
|
||||
private GitHub githubSequential; |
||||
|
||||
private MockClient mockClientSequential; |
||||
|
||||
@Before |
||||
public void setup() throws IOException { |
||||
try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { |
||||
byte[] data = toByteArray(input); |
||||
|
||||
mockClientSequential = new MockClient(true); |
||||
githubSequential = Feign.builder().decoder(new AssertionDecoder(new GsonDecoder())) |
||||
.client(mockClientSequential |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors", HttpsURLConnection.HTTP_OK, data) |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=55", |
||||
HttpsURLConnection.HTTP_NOT_FOUND) |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=7 7", |
||||
HttpsURLConnection.HTTP_INTERNAL_ERROR, new ByteArrayInputStream(data)) |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors", |
||||
Response.builder().status(HttpsURLConnection.HTTP_OK) |
||||
.headers(MockClient.EMPTY_HEADERS).body(data))) |
||||
.target(new MockTarget<>(GitHub.class)); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void sequentialRequests() throws Exception { |
||||
githubSequential.contributors("netflix", "feign"); |
||||
try { |
||||
githubSequential.contributors("55", "netflix", "feign"); |
||||
fail(); |
||||
} catch (FeignException e) { |
||||
assertThat(e.status(), equalTo(HttpsURLConnection.HTTP_NOT_FOUND)); |
||||
} |
||||
try { |
||||
githubSequential.contributors("7 7", "netflix", "feign"); |
||||
fail(); |
||||
} catch (FeignException e) { |
||||
assertThat(e.status(), equalTo(HttpsURLConnection.HTTP_INTERNAL_ERROR)); |
||||
} |
||||
githubSequential.contributors("netflix", "feign"); |
||||
|
||||
mockClientSequential.verifyStatus(); |
||||
} |
||||
|
||||
@Test |
||||
public void sequentialRequestsCalledTooLess() throws Exception { |
||||
githubSequential.contributors("netflix", "feign"); |
||||
try { |
||||
mockClientSequential.verifyStatus(); |
||||
fail(); |
||||
} catch (VerificationAssertionError e) { |
||||
assertThat(e.getMessage(), startsWith("More executions")); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void sequentialRequestsCalledTooMany() throws Exception { |
||||
sequentialRequests(); |
||||
|
||||
try { |
||||
githubSequential.contributors("netflix", "feign"); |
||||
fail(); |
||||
} catch (VerificationAssertionError e) { |
||||
assertThat(e.getMessage(), containsString("excessive")); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void sequentialRequestsInWrongOrder() throws Exception { |
||||
try { |
||||
githubSequential.contributors("7 7", "netflix", "feign"); |
||||
fail(); |
||||
} catch (VerificationAssertionError e) { |
||||
assertThat(e.getMessage(), startsWith("Expected Request [")); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,261 @@
@@ -0,0 +1,261 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
import static feign.Util.toByteArray; |
||||
import static org.hamcrest.MatcherAssert.assertThat; |
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.hasSize; |
||||
import static org.hamcrest.Matchers.notNullValue; |
||||
import static org.junit.Assert.fail; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.lang.reflect.Type; |
||||
import java.util.List; |
||||
|
||||
import javax.net.ssl.HttpsURLConnection; |
||||
|
||||
import org.hamcrest.Matchers; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import feign.Body; |
||||
import feign.Feign; |
||||
import feign.FeignException; |
||||
import feign.Param; |
||||
import feign.Request; |
||||
import feign.RequestLine; |
||||
import feign.Response; |
||||
import feign.codec.DecodeException; |
||||
import feign.codec.Decoder; |
||||
import feign.gson.GsonDecoder; |
||||
|
||||
public class MockClientTest { |
||||
|
||||
interface GitHub { |
||||
|
||||
@RequestLine("GET /repos/{owner}/{repo}/contributors") |
||||
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); |
||||
|
||||
@RequestLine("GET /repos/{owner}/{repo}/contributors?client_id={client_id}") |
||||
List<Contributor> contributors(@Param("client_id") String clientId, @Param("owner") String owner, |
||||
@Param("repo") String repo); |
||||
|
||||
@RequestLine("PATCH /repos/{owner}/{repo}/contributors") |
||||
List<Contributor> patchContributors(@Param("owner") String owner, @Param("repo") String repo); |
||||
|
||||
@RequestLine("POST /repos/{owner}/{repo}/contributors") |
||||
@Body("%7B\"login\":\"{login}\",\"type\":\"{type}\"%7D") |
||||
Contributor create(@Param("owner") String owner, @Param("repo") String repo, @Param("login") String login, |
||||
@Param("type") String type); |
||||
|
||||
} |
||||
|
||||
static class Contributor { |
||||
|
||||
String login; |
||||
|
||||
int contributions; |
||||
|
||||
} |
||||
|
||||
class AssertionDecoder implements Decoder { |
||||
|
||||
private final Decoder delegate; |
||||
|
||||
public AssertionDecoder(Decoder delegate) { |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
@Override |
||||
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { |
||||
assertThat(response.request(), notNullValue()); |
||||
|
||||
return delegate.decode(response, type); |
||||
} |
||||
|
||||
} |
||||
|
||||
private GitHub github; |
||||
|
||||
private MockClient mockClient; |
||||
|
||||
@Before |
||||
public void setup() throws IOException { |
||||
try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { |
||||
byte[] data = toByteArray(input); |
||||
mockClient = new MockClient(); |
||||
github = Feign.builder().decoder(new AssertionDecoder(new GsonDecoder())) |
||||
.client(mockClient.ok(HttpMethod.GET, "/repos/netflix/feign/contributors", data) |
||||
.ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=55") |
||||
.ok(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=7 7", |
||||
new ByteArrayInputStream(data)) |
||||
.ok(HttpMethod.POST, "/repos/netflix/feign/contributors", |
||||
"{\"login\":\"velo\",\"contributions\":0}") |
||||
.noContent(HttpMethod.PATCH, "/repos/velo/feign-mock/contributors") |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=1234567890", |
||||
HttpsURLConnection.HTTP_NOT_FOUND) |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", |
||||
HttpsURLConnection.HTTP_INTERNAL_ERROR, new ByteArrayInputStream(data)) |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", |
||||
HttpsURLConnection.HTTP_INTERNAL_ERROR, "") |
||||
.add(HttpMethod.GET, "/repos/netflix/feign/contributors?client_id=123456789", |
||||
HttpsURLConnection.HTTP_INTERNAL_ERROR, data)) |
||||
.target(new MockTarget<>(GitHub.class)); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void hitMock() { |
||||
List<Contributor> contributors = github.contributors("netflix", "feign"); |
||||
assertThat(contributors, hasSize(30)); |
||||
mockClient.verifyStatus(); |
||||
} |
||||
|
||||
@Test |
||||
public void missMock() { |
||||
try { |
||||
github.contributors("velo", "feign-mock"); |
||||
fail(); |
||||
} catch (FeignException e) { |
||||
assertThat(e.getMessage(), Matchers.containsString("404")); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void missHttpMethod() { |
||||
try { |
||||
github.patchContributors("netflix", "feign"); |
||||
fail(); |
||||
} catch (FeignException e) { |
||||
assertThat(e.getMessage(), Matchers.containsString("404")); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void paramsEncoding() { |
||||
List<Contributor> contributors = github.contributors("7 7", "netflix", "feign"); |
||||
assertThat(contributors, hasSize(30)); |
||||
mockClient.verifyStatus(); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyInvocation() { |
||||
Contributor contribution = github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); |
||||
// making sure it received a proper response
|
||||
assertThat(contribution, notNullValue()); |
||||
assertThat(contribution.login, equalTo("velo")); |
||||
assertThat(contribution.contributions, equalTo(0)); |
||||
|
||||
List<Request> results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); |
||||
assertThat(results, hasSize(1)); |
||||
|
||||
byte[] body = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors").body(); |
||||
assertThat(body, notNullValue()); |
||||
|
||||
String message = new String(body); |
||||
assertThat(message, containsString("velo_at_github")); |
||||
assertThat(message, containsString("preposterous hacker")); |
||||
|
||||
mockClient.verifyStatus(); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyNone() { |
||||
github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); |
||||
mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 1); |
||||
|
||||
try { |
||||
mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); |
||||
fail(); |
||||
} catch (VerificationAssertionError e) { |
||||
assertThat(e.getMessage(), containsString("Do not wanted")); |
||||
assertThat(e.getMessage(), containsString("POST")); |
||||
assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); |
||||
} |
||||
|
||||
try { |
||||
mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); |
||||
fail(); |
||||
} catch (VerificationAssertionError e) { |
||||
assertThat(e.getMessage(), containsString("Wanted")); |
||||
assertThat(e.getMessage(), containsString("POST")); |
||||
assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); |
||||
assertThat(e.getMessage(), containsString("'3'")); |
||||
assertThat(e.getMessage(), containsString("'1'")); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void verifyNotInvoked() { |
||||
mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); |
||||
List<Request> results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 0); |
||||
assertThat(results, hasSize(0)); |
||||
try { |
||||
mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); |
||||
fail(); |
||||
} catch (VerificationAssertionError e) { |
||||
assertThat(e.getMessage(), containsString("Wanted")); |
||||
assertThat(e.getMessage(), containsString("POST")); |
||||
assertThat(e.getMessage(), containsString("/repos/netflix/feign/contributors")); |
||||
assertThat(e.getMessage(), containsString("never invoked")); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void verifyNegative() { |
||||
try { |
||||
mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", -1); |
||||
fail(); |
||||
} catch (IllegalArgumentException e) { |
||||
assertThat(e.getMessage(), containsString("non negative")); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void verifyMultipleRequests() { |
||||
mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); |
||||
|
||||
github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); |
||||
Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); |
||||
assertThat(result, notNullValue()); |
||||
|
||||
github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); |
||||
List<Request> results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 2); |
||||
assertThat(results, hasSize(2)); |
||||
|
||||
github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); |
||||
results = mockClient.verifyTimes(HttpMethod.POST, "/repos/netflix/feign/contributors", 3); |
||||
assertThat(results, hasSize(3)); |
||||
|
||||
mockClient.verifyStatus(); |
||||
} |
||||
|
||||
@Test |
||||
public void resetRequests() { |
||||
mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); |
||||
|
||||
github.create("netflix", "feign", "velo_at_github", "preposterous hacker"); |
||||
Request result = mockClient.verifyOne(HttpMethod.POST, "/repos/netflix/feign/contributors"); |
||||
assertThat(result, notNullValue()); |
||||
|
||||
mockClient.resetRequests(); |
||||
|
||||
mockClient.verifyNever(HttpMethod.POST, "/repos/netflix/feign/contributors"); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
public class MockTargetTest { |
||||
|
||||
private MockTarget<MockTargetTest> target; |
||||
|
||||
@Before |
||||
public void setup() { |
||||
target = new MockTarget<>(MockTargetTest.class); |
||||
} |
||||
|
||||
@Test |
||||
public void test() { |
||||
assertThat(target.name(), equalTo("MockTargetTest")); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
/** |
||||
* Copyright 2012-2018 The Feign 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 feign.mock; |
||||
|
||||
import static org.hamcrest.Matchers.both; |
||||
import static org.hamcrest.Matchers.containsString; |
||||
import static org.hamcrest.Matchers.equalTo; |
||||
import static org.hamcrest.Matchers.hasSize; |
||||
import static org.hamcrest.Matchers.not; |
||||
import static org.hamcrest.Matchers.startsWith; |
||||
import static org.junit.Assert.assertThat; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import feign.Request; |
||||
|
||||
public class RequestKeyTest { |
||||
|
||||
private RequestKey requestKey; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
Map<String, Collection<String>> map = new HashMap<>(); |
||||
map.put("my-header", Arrays.asList("val")); |
||||
requestKey = RequestKey.builder(HttpMethod.GET, "a").headers(map).charset(StandardCharsets.UTF_16) |
||||
.body("content").build(); |
||||
} |
||||
|
||||
@Test |
||||
public void builder() throws Exception { |
||||
assertThat(requestKey.getMethod(), equalTo(HttpMethod.GET)); |
||||
assertThat(requestKey.getUrl(), equalTo("a")); |
||||
assertThat(requestKey.getHeaders().entrySet(), hasSize(1)); |
||||
assertThat(requestKey.getHeaders().get("my-header"), equalTo((Collection<String>) Arrays.asList("val"))); |
||||
assertThat(requestKey.getCharset(), equalTo(StandardCharsets.UTF_16)); |
||||
} |
||||
|
||||
@Test |
||||
public void create() throws Exception { |
||||
Map<String, Collection<String>> map = new HashMap<>(); |
||||
map.put("my-header", Arrays.asList("val")); |
||||
Request request = Request.create("GET", "a", map, "content".getBytes(StandardCharsets.UTF_8), |
||||
StandardCharsets.UTF_16); |
||||
requestKey = RequestKey.create(request); |
||||
|
||||
assertThat(requestKey.getMethod(), equalTo(HttpMethod.GET)); |
||||
assertThat(requestKey.getUrl(), equalTo("a")); |
||||
assertThat(requestKey.getHeaders().entrySet(), hasSize(1)); |
||||
assertThat(requestKey.getHeaders().get("my-header"), equalTo((Collection<String>) Arrays.asList("val"))); |
||||
assertThat(requestKey.getCharset(), equalTo(StandardCharsets.UTF_16)); |
||||
assertThat(requestKey.getBody(), equalTo("content".getBytes(StandardCharsets.UTF_8))); |
||||
} |
||||
|
||||
@Test |
||||
public void checkHashes() { |
||||
RequestKey requestKey1 = RequestKey.builder(HttpMethod.GET, "a").build(); |
||||
RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "b").build(); |
||||
|
||||
assertThat(requestKey1.hashCode(), not(equalTo(requestKey2.hashCode()))); |
||||
assertThat(requestKey1, not(equalTo(requestKey2))); |
||||
} |
||||
|
||||
@Test |
||||
public void equalObject() { |
||||
assertThat(requestKey, not(equalTo(new Object()))); |
||||
} |
||||
|
||||
@Test |
||||
public void equalNull() { |
||||
assertThat(requestKey, not(equalTo(null))); |
||||
} |
||||
|
||||
@Test |
||||
public void equalPost() { |
||||
RequestKey requestKey1 = RequestKey.builder(HttpMethod.GET, "a").build(); |
||||
RequestKey requestKey2 = RequestKey.builder(HttpMethod.POST, "a").build(); |
||||
|
||||
assertThat(requestKey1.hashCode(), not(equalTo(requestKey2.hashCode()))); |
||||
assertThat(requestKey1, not(equalTo(requestKey2))); |
||||
} |
||||
|
||||
@Test |
||||
public void equalSelf() { |
||||
assertThat(requestKey.hashCode(), equalTo(requestKey.hashCode())); |
||||
assertThat(requestKey, equalTo(requestKey)); |
||||
} |
||||
|
||||
@Test |
||||
public void equalMinimum() { |
||||
RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").build(); |
||||
|
||||
assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); |
||||
assertThat(requestKey, equalTo(requestKey2)); |
||||
} |
||||
|
||||
@Test |
||||
public void equalExtra() { |
||||
Map<String, Collection<String>> map = new HashMap<>(); |
||||
map.put("my-other-header", Arrays.asList("other value")); |
||||
RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").headers(map) |
||||
.charset(StandardCharsets.ISO_8859_1).build(); |
||||
|
||||
assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); |
||||
assertThat(requestKey, equalTo(requestKey2)); |
||||
} |
||||
|
||||
@Test |
||||
public void equalsExtended() { |
||||
RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").build(); |
||||
|
||||
assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); |
||||
assertThat(requestKey.equalsExtended(requestKey2), equalTo(true)); |
||||
} |
||||
|
||||
@Test |
||||
public void equalsExtendedExtra() { |
||||
Map<String, Collection<String>> map = new HashMap<>(); |
||||
map.put("my-other-header", Arrays.asList("other value")); |
||||
RequestKey requestKey2 = RequestKey.builder(HttpMethod.GET, "a").headers(map) |
||||
.charset(StandardCharsets.ISO_8859_1).build(); |
||||
|
||||
assertThat(requestKey.hashCode(), equalTo(requestKey2.hashCode())); |
||||
assertThat(requestKey.equalsExtended(requestKey2), equalTo(false)); |
||||
} |
||||
|
||||
@Test |
||||
public void testToString() throws Exception { |
||||
assertThat(requestKey.toString(), startsWith("Request [GET a: ")); |
||||
assertThat(requestKey.toString(), both(containsString(" with 1 ")).and(containsString(" UTF-16]"))); |
||||
} |
||||
|
||||
@Test |
||||
public void testToStringSimple() throws Exception { |
||||
requestKey = RequestKey.builder(HttpMethod.GET, "a").build(); |
||||
|
||||
assertThat(requestKey.toString(), startsWith("Request [GET a: ")); |
||||
assertThat(requestKey.toString(), both(containsString(" without ")).and(containsString(" no charset"))); |
||||
} |
||||
|
||||
} |
||||
//
|
@ -0,0 +1,602 @@
@@ -0,0 +1,602 @@
|
||||
[ |
||||
{ |
||||
"login": "adriancole", |
||||
"id": 64215, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/64215?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/adriancole", |
||||
"html_url": "https://github.com/adriancole", |
||||
"followers_url": "https://api.github.com/users/adriancole/followers", |
||||
"following_url": "https://api.github.com/users/adriancole/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/adriancole/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/adriancole/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/adriancole/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/adriancole/orgs", |
||||
"repos_url": "https://api.github.com/users/adriancole/repos", |
||||
"events_url": "https://api.github.com/users/adriancole/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/adriancole/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 297 |
||||
}, |
||||
{ |
||||
"login": "quidryan", |
||||
"id": 360255, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/360255?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/quidryan", |
||||
"html_url": "https://github.com/quidryan", |
||||
"followers_url": "https://api.github.com/users/quidryan/followers", |
||||
"following_url": "https://api.github.com/users/quidryan/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/quidryan/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/quidryan/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/quidryan/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/quidryan/orgs", |
||||
"repos_url": "https://api.github.com/users/quidryan/repos", |
||||
"events_url": "https://api.github.com/users/quidryan/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/quidryan/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 43 |
||||
}, |
||||
{ |
||||
"login": "rspieldenner", |
||||
"id": 782102, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/782102?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/rspieldenner", |
||||
"html_url": "https://github.com/rspieldenner", |
||||
"followers_url": "https://api.github.com/users/rspieldenner/followers", |
||||
"following_url": "https://api.github.com/users/rspieldenner/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/rspieldenner/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/rspieldenner/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/rspieldenner/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/rspieldenner/orgs", |
||||
"repos_url": "https://api.github.com/users/rspieldenner/repos", |
||||
"events_url": "https://api.github.com/users/rspieldenner/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/rspieldenner/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 14 |
||||
}, |
||||
{ |
||||
"login": "davidmc24", |
||||
"id": 447825, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/447825?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/davidmc24", |
||||
"html_url": "https://github.com/davidmc24", |
||||
"followers_url": "https://api.github.com/users/davidmc24/followers", |
||||
"following_url": "https://api.github.com/users/davidmc24/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/davidmc24/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/davidmc24/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/davidmc24/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/davidmc24/orgs", |
||||
"repos_url": "https://api.github.com/users/davidmc24/repos", |
||||
"events_url": "https://api.github.com/users/davidmc24/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/davidmc24/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 12 |
||||
}, |
||||
{ |
||||
"login": "ahus1", |
||||
"id": 3957921, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3957921?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/ahus1", |
||||
"html_url": "https://github.com/ahus1", |
||||
"followers_url": "https://api.github.com/users/ahus1/followers", |
||||
"following_url": "https://api.github.com/users/ahus1/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/ahus1/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/ahus1/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/ahus1/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/ahus1/orgs", |
||||
"repos_url": "https://api.github.com/users/ahus1/repos", |
||||
"events_url": "https://api.github.com/users/ahus1/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/ahus1/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 6 |
||||
}, |
||||
{ |
||||
"login": "allenxwang", |
||||
"id": 1728105, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1728105?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/allenxwang", |
||||
"html_url": "https://github.com/allenxwang", |
||||
"followers_url": "https://api.github.com/users/allenxwang/followers", |
||||
"following_url": "https://api.github.com/users/allenxwang/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/allenxwang/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/allenxwang/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/allenxwang/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/allenxwang/orgs", |
||||
"repos_url": "https://api.github.com/users/allenxwang/repos", |
||||
"events_url": "https://api.github.com/users/allenxwang/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/allenxwang/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 5 |
||||
}, |
||||
{ |
||||
"login": "nmiyake", |
||||
"id": 4267425, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4267425?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/nmiyake", |
||||
"html_url": "https://github.com/nmiyake", |
||||
"followers_url": "https://api.github.com/users/nmiyake/followers", |
||||
"following_url": "https://api.github.com/users/nmiyake/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/nmiyake/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/nmiyake/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/nmiyake/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/nmiyake/orgs", |
||||
"repos_url": "https://api.github.com/users/nmiyake/repos", |
||||
"events_url": "https://api.github.com/users/nmiyake/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/nmiyake/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 4 |
||||
}, |
||||
{ |
||||
"login": "Drdoteam", |
||||
"id": 4572139, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4572139?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/Drdoteam", |
||||
"html_url": "https://github.com/Drdoteam", |
||||
"followers_url": "https://api.github.com/users/Drdoteam/followers", |
||||
"following_url": "https://api.github.com/users/Drdoteam/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/Drdoteam/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/Drdoteam/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/Drdoteam/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/Drdoteam/orgs", |
||||
"repos_url": "https://api.github.com/users/Drdoteam/repos", |
||||
"events_url": "https://api.github.com/users/Drdoteam/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/Drdoteam/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 4 |
||||
}, |
||||
{ |
||||
"login": "spencergibb", |
||||
"id": 594085, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/594085?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/spencergibb", |
||||
"html_url": "https://github.com/spencergibb", |
||||
"followers_url": "https://api.github.com/users/spencergibb/followers", |
||||
"following_url": "https://api.github.com/users/spencergibb/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/spencergibb/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/spencergibb/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/spencergibb/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/spencergibb/orgs", |
||||
"repos_url": "https://api.github.com/users/spencergibb/repos", |
||||
"events_url": "https://api.github.com/users/spencergibb/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/spencergibb/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 3 |
||||
}, |
||||
{ |
||||
"login": "jacob-meacham", |
||||
"id": 1624811, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1624811?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/jacob-meacham", |
||||
"html_url": "https://github.com/jacob-meacham", |
||||
"followers_url": "https://api.github.com/users/jacob-meacham/followers", |
||||
"following_url": "https://api.github.com/users/jacob-meacham/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/jacob-meacham/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/jacob-meacham/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/jacob-meacham/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/jacob-meacham/orgs", |
||||
"repos_url": "https://api.github.com/users/jacob-meacham/repos", |
||||
"events_url": "https://api.github.com/users/jacob-meacham/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/jacob-meacham/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 3 |
||||
}, |
||||
{ |
||||
"login": "pnepywoda", |
||||
"id": 13909400, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13909400?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/pnepywoda", |
||||
"html_url": "https://github.com/pnepywoda", |
||||
"followers_url": "https://api.github.com/users/pnepywoda/followers", |
||||
"following_url": "https://api.github.com/users/pnepywoda/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/pnepywoda/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/pnepywoda/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/pnepywoda/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/pnepywoda/orgs", |
||||
"repos_url": "https://api.github.com/users/pnepywoda/repos", |
||||
"events_url": "https://api.github.com/users/pnepywoda/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/pnepywoda/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 3 |
||||
}, |
||||
{ |
||||
"login": "santhosh-tekuri", |
||||
"id": 1112271, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1112271?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/santhosh-tekuri", |
||||
"html_url": "https://github.com/santhosh-tekuri", |
||||
"followers_url": "https://api.github.com/users/santhosh-tekuri/followers", |
||||
"following_url": "https://api.github.com/users/santhosh-tekuri/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/santhosh-tekuri/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/santhosh-tekuri/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/santhosh-tekuri/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/santhosh-tekuri/orgs", |
||||
"repos_url": "https://api.github.com/users/santhosh-tekuri/repos", |
||||
"events_url": "https://api.github.com/users/santhosh-tekuri/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/santhosh-tekuri/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 3 |
||||
}, |
||||
{ |
||||
"login": "bstick12", |
||||
"id": 1146861, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1146861?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/bstick12", |
||||
"html_url": "https://github.com/bstick12", |
||||
"followers_url": "https://api.github.com/users/bstick12/followers", |
||||
"following_url": "https://api.github.com/users/bstick12/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/bstick12/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/bstick12/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/bstick12/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/bstick12/orgs", |
||||
"repos_url": "https://api.github.com/users/bstick12/repos", |
||||
"events_url": "https://api.github.com/users/bstick12/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/bstick12/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 3 |
||||
}, |
||||
{ |
||||
"login": "oillio", |
||||
"id": 205051, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/205051?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/oillio", |
||||
"html_url": "https://github.com/oillio", |
||||
"followers_url": "https://api.github.com/users/oillio/followers", |
||||
"following_url": "https://api.github.com/users/oillio/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/oillio/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/oillio/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/oillio/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/oillio/orgs", |
||||
"repos_url": "https://api.github.com/users/oillio/repos", |
||||
"events_url": "https://api.github.com/users/oillio/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/oillio/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 2 |
||||
}, |
||||
{ |
||||
"login": "stromnet", |
||||
"id": 668449, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/668449?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/stromnet", |
||||
"html_url": "https://github.com/stromnet", |
||||
"followers_url": "https://api.github.com/users/stromnet/followers", |
||||
"following_url": "https://api.github.com/users/stromnet/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/stromnet/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/stromnet/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/stromnet/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/stromnet/orgs", |
||||
"repos_url": "https://api.github.com/users/stromnet/repos", |
||||
"events_url": "https://api.github.com/users/stromnet/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/stromnet/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 2 |
||||
}, |
||||
{ |
||||
"login": "qualidafial", |
||||
"id": 38629, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38629?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/qualidafial", |
||||
"html_url": "https://github.com/qualidafial", |
||||
"followers_url": "https://api.github.com/users/qualidafial/followers", |
||||
"following_url": "https://api.github.com/users/qualidafial/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/qualidafial/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/qualidafial/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/qualidafial/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/qualidafial/orgs", |
||||
"repos_url": "https://api.github.com/users/qualidafial/repos", |
||||
"events_url": "https://api.github.com/users/qualidafial/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/qualidafial/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 2 |
||||
}, |
||||
{ |
||||
"login": "amit-git", |
||||
"id": 2767034, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2767034?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/amit-git", |
||||
"html_url": "https://github.com/amit-git", |
||||
"followers_url": "https://api.github.com/users/amit-git/followers", |
||||
"following_url": "https://api.github.com/users/amit-git/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/amit-git/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/amit-git/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/amit-git/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/amit-git/orgs", |
||||
"repos_url": "https://api.github.com/users/amit-git/repos", |
||||
"events_url": "https://api.github.com/users/amit-git/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/amit-git/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 2 |
||||
}, |
||||
{ |
||||
"login": "dstepanov", |
||||
"id": 666879, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/666879?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/dstepanov", |
||||
"html_url": "https://github.com/dstepanov", |
||||
"followers_url": "https://api.github.com/users/dstepanov/followers", |
||||
"following_url": "https://api.github.com/users/dstepanov/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/dstepanov/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/dstepanov/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/dstepanov/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/dstepanov/orgs", |
||||
"repos_url": "https://api.github.com/users/dstepanov/repos", |
||||
"events_url": "https://api.github.com/users/dstepanov/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/dstepanov/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "asukhyy", |
||||
"id": 891597, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/891597?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/asukhyy", |
||||
"html_url": "https://github.com/asukhyy", |
||||
"followers_url": "https://api.github.com/users/asukhyy/followers", |
||||
"following_url": "https://api.github.com/users/asukhyy/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/asukhyy/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/asukhyy/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/asukhyy/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/asukhyy/orgs", |
||||
"repos_url": "https://api.github.com/users/asukhyy/repos", |
||||
"events_url": "https://api.github.com/users/asukhyy/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/asukhyy/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "carlossg", |
||||
"id": 23651, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23651?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/carlossg", |
||||
"html_url": "https://github.com/carlossg", |
||||
"followers_url": "https://api.github.com/users/carlossg/followers", |
||||
"following_url": "https://api.github.com/users/carlossg/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/carlossg/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/carlossg/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/carlossg/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/carlossg/orgs", |
||||
"repos_url": "https://api.github.com/users/carlossg/repos", |
||||
"events_url": "https://api.github.com/users/carlossg/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/carlossg/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "christopherlakey", |
||||
"id": 1859690, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1859690?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/christopherlakey", |
||||
"html_url": "https://github.com/christopherlakey", |
||||
"followers_url": "https://api.github.com/users/christopherlakey/followers", |
||||
"following_url": "https://api.github.com/users/christopherlakey/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/christopherlakey/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/christopherlakey/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/christopherlakey/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/christopherlakey/orgs", |
||||
"repos_url": "https://api.github.com/users/christopherlakey/repos", |
||||
"events_url": "https://api.github.com/users/christopherlakey/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/christopherlakey/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "dsyer", |
||||
"id": 124075, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/124075?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/dsyer", |
||||
"html_url": "https://github.com/dsyer", |
||||
"followers_url": "https://api.github.com/users/dsyer/followers", |
||||
"following_url": "https://api.github.com/users/dsyer/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/dsyer/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/dsyer/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/dsyer/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/dsyer/orgs", |
||||
"repos_url": "https://api.github.com/users/dsyer/repos", |
||||
"events_url": "https://api.github.com/users/dsyer/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/dsyer/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "aspyker", |
||||
"id": 260750, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/260750?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/aspyker", |
||||
"html_url": "https://github.com/aspyker", |
||||
"followers_url": "https://api.github.com/users/aspyker/followers", |
||||
"following_url": "https://api.github.com/users/aspyker/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/aspyker/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/aspyker/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/aspyker/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/aspyker/orgs", |
||||
"repos_url": "https://api.github.com/users/aspyker/repos", |
||||
"events_url": "https://api.github.com/users/aspyker/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/aspyker/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "FrEaKmAn", |
||||
"id": 232901, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/232901?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/FrEaKmAn", |
||||
"html_url": "https://github.com/FrEaKmAn", |
||||
"followers_url": "https://api.github.com/users/FrEaKmAn/followers", |
||||
"following_url": "https://api.github.com/users/FrEaKmAn/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/FrEaKmAn/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/FrEaKmAn/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/FrEaKmAn/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/FrEaKmAn/orgs", |
||||
"repos_url": "https://api.github.com/users/FrEaKmAn/repos", |
||||
"events_url": "https://api.github.com/users/FrEaKmAn/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/FrEaKmAn/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "htynkn", |
||||
"id": 659135, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/659135?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/htynkn", |
||||
"html_url": "https://github.com/htynkn", |
||||
"followers_url": "https://api.github.com/users/htynkn/followers", |
||||
"following_url": "https://api.github.com/users/htynkn/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/htynkn/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/htynkn/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/htynkn/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/htynkn/orgs", |
||||
"repos_url": "https://api.github.com/users/htynkn/repos", |
||||
"events_url": "https://api.github.com/users/htynkn/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/htynkn/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "jebeaudet", |
||||
"id": 3722096, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3722096?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/jebeaudet", |
||||
"html_url": "https://github.com/jebeaudet", |
||||
"followers_url": "https://api.github.com/users/jebeaudet/followers", |
||||
"following_url": "https://api.github.com/users/jebeaudet/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/jebeaudet/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/jebeaudet/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/jebeaudet/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/jebeaudet/orgs", |
||||
"repos_url": "https://api.github.com/users/jebeaudet/repos", |
||||
"events_url": "https://api.github.com/users/jebeaudet/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/jebeaudet/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "jmcampanini", |
||||
"id": 316848, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/316848?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/jmcampanini", |
||||
"html_url": "https://github.com/jmcampanini", |
||||
"followers_url": "https://api.github.com/users/jmcampanini/followers", |
||||
"following_url": "https://api.github.com/users/jmcampanini/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/jmcampanini/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/jmcampanini/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/jmcampanini/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/jmcampanini/orgs", |
||||
"repos_url": "https://api.github.com/users/jmcampanini/repos", |
||||
"events_url": "https://api.github.com/users/jmcampanini/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/jmcampanini/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "Randgalt", |
||||
"id": 264818, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/264818?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/Randgalt", |
||||
"html_url": "https://github.com/Randgalt", |
||||
"followers_url": "https://api.github.com/users/Randgalt/followers", |
||||
"following_url": "https://api.github.com/users/Randgalt/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/Randgalt/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/Randgalt/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/Randgalt/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/Randgalt/orgs", |
||||
"repos_url": "https://api.github.com/users/Randgalt/repos", |
||||
"events_url": "https://api.github.com/users/Randgalt/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/Randgalt/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "VanRoy", |
||||
"id": 1958756, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1958756?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/VanRoy", |
||||
"html_url": "https://github.com/VanRoy", |
||||
"followers_url": "https://api.github.com/users/VanRoy/followers", |
||||
"following_url": "https://api.github.com/users/VanRoy/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/VanRoy/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/VanRoy/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/VanRoy/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/VanRoy/orgs", |
||||
"repos_url": "https://api.github.com/users/VanRoy/repos", |
||||
"events_url": "https://api.github.com/users/VanRoy/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/VanRoy/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
}, |
||||
{ |
||||
"login": "mhurne", |
||||
"id": 677354, |
||||
"avatar_url": "https://avatars.githubusercontent.com/u/677354?v=3", |
||||
"gravatar_id": "", |
||||
"url": "https://api.github.com/users/mhurne", |
||||
"html_url": "https://github.com/mhurne", |
||||
"followers_url": "https://api.github.com/users/mhurne/followers", |
||||
"following_url": "https://api.github.com/users/mhurne/following{/other_user}", |
||||
"gists_url": "https://api.github.com/users/mhurne/gists{/gist_id}", |
||||
"starred_url": "https://api.github.com/users/mhurne/starred{/owner}{/repo}", |
||||
"subscriptions_url": "https://api.github.com/users/mhurne/subscriptions", |
||||
"organizations_url": "https://api.github.com/users/mhurne/orgs", |
||||
"repos_url": "https://api.github.com/users/mhurne/repos", |
||||
"events_url": "https://api.github.com/users/mhurne/events{/privacy}", |
||||
"received_events_url": "https://api.github.com/users/mhurne/received_events", |
||||
"type": "User", |
||||
"site_admin": false, |
||||
"contributions": 1 |
||||
} |
||||
] |
Loading…
Reference in new issue