Skip to content

Commit

Permalink
Add a test for failing requests by making ReturnOrWebsocketable als…
Browse files Browse the repository at this point in the history
…o `Failable`
  • Loading branch information
SamBarker committed Jul 26, 2024
1 parent a3598b6 commit 7883832
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import io.fabric8.kubernetes.client.http.AbstractHttpPostTest;
import io.fabric8.kubernetes.client.http.HttpClient;

import java.io.IOException;

@SuppressWarnings("java:S2187")
public class JdkHttpClientPostTest extends AbstractHttpPostTest {
@Override
Expand All @@ -31,4 +33,9 @@ public void expectContinue() {
// Apparently the JDK sets the Expect header to 100-Continue which is not according to spec.
// We should consider overriding the header manually instead.
}

@Override
protected Class<? extends Exception> getConnectionFailedExceptionType() {
return IOException.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
import io.fabric8.kubernetes.client.http.AbstractHttpPostTest;
import io.fabric8.kubernetes.client.http.HttpClient;

import java.io.EOFException;

@SuppressWarnings("java:S2187")
public class JettyHttpPostTest extends AbstractHttpPostTest {
@Override
protected HttpClient.Factory getHttpClientFactory() {
return new JettyHttpClientFactory();
}

@Override
protected Class<? extends Exception> getConnectionFailedExceptionType() {
return EOFException.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
import io.fabric8.kubernetes.client.http.AbstractHttpPostTest;
import io.fabric8.kubernetes.client.http.HttpClient;

import java.net.ConnectException;

@SuppressWarnings("java:S2187")
public class OkHttpPostTest extends AbstractHttpPostTest {
@Override
protected HttpClient.Factory getHttpClientFactory() {
return new OkHttpClientFactory();
}

@Override
protected Class<? extends Exception> getConnectionFailedExceptionType() {
return ConnectException.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.fabric8.kubernetes.client.http.AbstractHttpPostTest;
import io.fabric8.kubernetes.client.http.HttpClient;
import io.vertx.core.http.HttpClosedException;

@SuppressWarnings("java:S2187")
public class VertxHttpClientPostTest extends AbstractHttpPostTest {
Expand All @@ -25,4 +26,9 @@ protected HttpClient.Factory getHttpClientFactory() {
return new VertxHttpClientFactory();
}

@Override
protected Class<? extends Exception> getConnectionFailedExceptionType() {
return HttpClosedException.class;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
*/
package io.fabric8.mockwebserver.dsl;

public interface ReturnOrWebsocketable<T> extends Returnable<T>, WebSocketable<WebSocketSessionBuilder<T>>, Replyable<T> {
public interface ReturnOrWebsocketable<T>
extends Returnable<T>, WebSocketable<WebSocketSessionBuilder<T>>, Replyable<T>, Failable<T> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
import io.fabric8.mockwebserver.utils.ResponseProvider;
import io.fabric8.mockwebserver.utils.ResponseProviders;
import okhttp3.Headers;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
import okhttp3.mockwebserver.SocketPolicy;

import java.util.ArrayDeque;
import java.util.ArrayList;
Expand All @@ -57,6 +59,7 @@ public class MockServerExpectationImpl implements MockServerExpectation {
private final int times;

private final Map<ServerRequest, Queue<ServerResponse>> responses;
private final boolean failing;

public MockServerExpectationImpl(Map<ServerRequest, Queue<ServerResponse>> responses, Context context) {
this(context, HttpMethod.ANY, null, 200, null, null, 0, TimeUnit.SECONDS, 1, responses);
Expand All @@ -65,12 +68,12 @@ public MockServerExpectationImpl(Map<ServerRequest, Queue<ServerResponse>> respo
public MockServerExpectationImpl(Context context, HttpMethod method, String path, int statusCode, String body,
String[] chunks, long delay, TimeUnit delayUnit, int times, Map<ServerRequest, Queue<ServerResponse>> responses) {
this(context, method, path, ResponseProviders.of(statusCode, body), ResponseProviders.ofAll(statusCode, chunks), delay,
delayUnit, times, responses);
delayUnit, times, responses, false);
}

public MockServerExpectationImpl(Context context, HttpMethod method, String path, ResponseProvider<String> bodyProvider,
ResponseProvider<List<String>> chunksProvider, long delay, TimeUnit delayUnit, int times,
Map<ServerRequest, Queue<ServerResponse>> responses) {
Map<ServerRequest, Queue<ServerResponse>> responses, boolean failing) {
this.context = context;
this.method = method;
this.path = path;
Expand All @@ -80,54 +83,55 @@ public MockServerExpectationImpl(Context context, HttpMethod method, String path
this.delayUnit = delayUnit;
this.times = times;
this.responses = responses;
this.failing = failing;
}

@Override
public DelayPathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> any() {
return new MockServerExpectationImpl(context, HttpMethod.ANY, path, bodyProvider, chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public DelayPathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> post() {
return new MockServerExpectationImpl(context, HttpMethod.POST, path, bodyProvider, chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public DelayPathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> get() {
return new MockServerExpectationImpl(context, HttpMethod.GET, path, bodyProvider, chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public DelayPathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> put() {
return new MockServerExpectationImpl(context, HttpMethod.PUT, path, bodyProvider, chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public DelayPathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> delete() {
return new MockServerExpectationImpl(context, HttpMethod.DELETE, path, bodyProvider, chunksProvider, delay, delayUnit,
times, responses);
times, responses, false);
}

@Override
public DelayPathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> patch() {
return new MockServerExpectationImpl(context, HttpMethod.PATCH, path, bodyProvider, chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>> withPath(String path) {
return new MockServerExpectationImpl(context, method, path, bodyProvider, chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public TimesOnceableOrHttpHeaderable<Void> andReturn(int statusCode, Object content) {
return new MockServerExpectationImpl(context, method, path, ResponseProviders.of(statusCode, toString(content)),
chunksProvider, delay, delayUnit, times, responses);
chunksProvider, delay, delayUnit, times, responses, false);
}

@Override
Expand All @@ -138,13 +142,13 @@ public TimesOnceableOrHttpHeaderable<Void> andReply(int statusCode, BodyProvider
@Override
public TimesOnceableOrHttpHeaderable<Void> andReply(ResponseProvider<Object> content) {
return new MockServerExpectationImpl(context, method, path, toString(content), chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public TimesOnceableOrHttpHeaderable<Void> andReturnChunked(int statusCode, Object... contents) {
return new MockServerExpectationImpl(context, method, path, bodyProvider,
ResponseProviders.of(statusCode, toString(contents)), delay, delayUnit, times, responses);
ResponseProviders.of(statusCode, toString(contents)), delay, delayUnit, times, responses, false);
}

@Override
Expand All @@ -155,7 +159,7 @@ public TimesOnceableOrHttpHeaderable<Void> andReplyChunked(int statusCode, BodyP
@Override
public TimesOnceableOrHttpHeaderable<Void> andReplyChunked(ResponseProvider<List<Object>> contents) {
return new MockServerExpectationImpl(context, method, path, bodyProvider, listToString(contents), delay, delayUnit, times,
responses);
responses, false);
}

@Override
Expand All @@ -181,13 +185,13 @@ public Void times(int times) {
@Override
public Pathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> delay(long delay, TimeUnit delayUnit) {
return new MockServerExpectationImpl(context, method, path, bodyProvider, chunksProvider, delay, delayUnit, times,
responses);
responses, false);
}

@Override
public Pathable<ReturnOrWebsocketable<TimesOnceableOrHttpHeaderable<Void>>> delay(long delayInMilliseconds) {
return new MockServerExpectationImpl(context, method, path, bodyProvider, chunksProvider, delayInMilliseconds,
TimeUnit.MILLISECONDS, times, responses);
TimeUnit.MILLISECONDS, times, responses, false);
}

@Override
Expand All @@ -207,14 +211,20 @@ public WebSocketSessionBuilder<TimesOnceableOrHttpHeaderable<Void>> andUpgradeTo
public TimesOnceableOrHttpHeaderable<Void> withHeader(String header) {
bodyProvider.setHeaders(bodyProvider.getHeaders().newBuilder().add(header).build());
return new MockServerExpectationImpl(context, method, path, bodyProvider, chunksProvider, delay, TimeUnit.MILLISECONDS,
times, responses);
times, responses, false);
}

@Override
public TimesOnceableOrHttpHeaderable<Void> withHeader(String name, String value) {
bodyProvider.setHeaders(bodyProvider.getHeaders().newBuilder().add(name, value).build());
return new MockServerExpectationImpl(context, method, path, bodyProvider, chunksProvider, delay, TimeUnit.MILLISECONDS,
times, responses);
times, responses, false);
}

@Override
public TimesOnceableOrHttpHeaderable<Void> failure(Object response, Exception e) {
return new MockServerExpectationImpl(context, method, path, bodyProvider, chunksProvider, delay, TimeUnit.MILLISECONDS,
times, responses, true);
}

private void enqueue(ServerRequest req, ServerResponse resp) {
Expand All @@ -224,6 +234,21 @@ private void enqueue(ServerRequest req, ServerResponse resp) {
private ServerResponse createResponse(boolean repeatable, long delay, TimeUnit delayUnit) {
if (chunksProvider != null) {
return new ChunkedResponse(repeatable, delay, delayUnit, chunksProvider);
} else if (failing) {
return new ServerResponse() {
@Override
public boolean isRepeatable() {
return repeatable;
}

@Override
public MockResponse toMockResponse(RecordedRequest recordedRequest) {
MockResponse mockResponse = new MockResponse();
mockResponse.setResponseCode(500);
mockResponse.setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST);
return mockResponse;
}
};
} else {
return new SimpleResponse(repeatable, bodyProvider, null, delay, delayUnit);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -47,6 +48,8 @@ static void afterAll() {

protected abstract HttpClient.Factory getHttpClientFactory();

protected abstract Class<? extends Exception> getConnectionFailedExceptionType();

@Test
@DisplayName("String body, should send a POST request with body")
public void postStringBody() throws Exception {
Expand Down Expand Up @@ -148,4 +151,23 @@ public void expectContinue() throws Exception {
.isEqualTo("100-continue");
}
}

@Test
public void expectFailure() {
server.expect().post().withPath("/post-failing").failure(null, new IllegalStateException("kaboom")).always();
// When
try (HttpClient client = getHttpClientFactory().newBuilder().build()) {
final CompletableFuture<HttpResponse<String>> response = client
.sendAsync(client.newHttpRequestBuilder()
.post(Collections.emptyMap())
.uri(server.url("/post-failing"))
.timeout(250, TimeUnit.MILLISECONDS)
.build(), String.class);

// Then
assertThat(response).failsWithin(30, TimeUnit.SECONDS)
.withThrowableOfType(ExecutionException.class)
.withCauseInstanceOf(getConnectionFailedExceptionType());
}
}
}

0 comments on commit 7883832

Please sign in to comment.