Skip to content

Commit da107fc

Browse files
committed
test: More tests and minor refactoring
1 parent f736c36 commit da107fc

File tree

4 files changed

+134
-81
lines changed

4 files changed

+134
-81
lines changed

src/main/java/com/spotify/github/http/okhttp/OkHttpHttpClient.java

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import static okhttp3.MediaType.parse;
2424

25+
import com.google.common.annotations.VisibleForTesting;
2526
import com.spotify.github.http.HttpClient;
2627
import com.spotify.github.http.HttpRequest;
2728
import com.spotify.github.http.HttpResponse;
@@ -75,23 +76,27 @@ public CompletableFuture<HttpResponse> send(final HttpRequest httpRequest) {
7576
if (this.callFactory == null) {
7677
this.callFactory = createTracedClient();
7778
}
78-
this.callFactory
79-
.newCall(request)
80-
.enqueue(
81-
new Callback() {
82-
83-
@Override
84-
public void onResponse(@NotNull final Call call, @NotNull final Response response)
85-
throws IOException {
86-
future.complete(new OkHttpHttpResponse(httpRequest, response));
87-
}
88-
89-
@Override
90-
public void onFailure(@NotNull final Call call, @NotNull final IOException e) {
91-
future.completeExceptionally(e);
92-
}
93-
});
9479
tracer.attachSpanToFuture(span, future);
80+
try {
81+
this.callFactory
82+
.newCall(request)
83+
.enqueue(
84+
new Callback() {
85+
86+
@Override
87+
public void onResponse(@NotNull final Call call, @NotNull final Response response)
88+
throws IOException {
89+
future.complete(new OkHttpHttpResponse(httpRequest, response));
90+
}
91+
92+
@Override
93+
public void onFailure(@NotNull final Call call, @NotNull final IOException e) {
94+
future.completeExceptionally(e);
95+
}
96+
});
97+
} catch (Exception e) {
98+
future.completeExceptionally(e);
99+
}
95100
}
96101
return future;
97102
}
@@ -164,7 +169,7 @@ private Call.Factory createTracedClient() {
164169
*
165170
* @return the traced client
166171
*/
167-
private Call.Factory createTracedClientNoopTracer() {
172+
protected Call.Factory createTracedClientNoopTracer() {
168173
return new Call.Factory() {
169174
@NotNull
170175
@Override
@@ -179,7 +184,8 @@ public Call newCall(@NotNull final Request request) {
179184
*
180185
* @return the traced client
181186
*/
182-
private Call.Factory createTracedClientOpenTelemetry() {
187+
@VisibleForTesting
188+
protected Call.Factory createTracedClientOpenTelemetry() {
183189
// OkHttpTelemetry is a helper class that provides a Call.Factory that can be used to trace
184190
return OkHttpTelemetry.builder(((OpenTelemetryTracer) this.tracer).getOpenTelemetry())
185191
.build()
@@ -191,7 +197,7 @@ private Call.Factory createTracedClientOpenTelemetry() {
191197
*
192198
* @return the traced client
193199
*/
194-
private Call.Factory createTracedClientOpenCensus() {
200+
protected Call.Factory createTracedClientOpenCensus() {
195201
return new Call.Factory() {
196202
@NotNull
197203
@Override

src/main/java/com/spotify/github/tracing/BaseTracer.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,21 @@ public Span span(final HttpRequest request, final CompletionStage<?> future) {
5353

5454
@Override
5555
public void attachSpanToFuture(final Span span, final CompletionStage<?> future) {
56-
future.whenComplete(
57-
(result, t) -> {
58-
if (t == null) {
59-
span.success();
60-
} else {
61-
span.failure(t);
62-
}
63-
span.close();
64-
});
56+
future
57+
.whenComplete(
58+
(result, t) -> {
59+
if (t == null) {
60+
span.success();
61+
} else {
62+
span.failure(t);
63+
}
64+
span.close();
65+
})
66+
.exceptionally(
67+
t -> {
68+
span.failure(t);
69+
span.close();
70+
return null;
71+
});
6572
}
6673
}

src/main/java/com/spotify/github/tracing/NoopTracer.java

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@
2020

2121
package com.spotify.github.tracing;
2222

23-
import static java.util.Objects.requireNonNull;
2423

2524
import com.spotify.github.http.HttpRequest;
2625
import java.util.concurrent.CompletionStage;
2726

28-
public class NoopTracer implements Tracer {
27+
public class NoopTracer extends BaseTracer {
2928

3029
public static final NoopTracer INSTANCE = new NoopTracer();
3130
private static final Span SPAN =
@@ -72,37 +71,12 @@ public Span addEvent(final String description) {
7271
private NoopTracer() {}
7372

7473
@Override
75-
public Span span(final String path, final String method, final CompletionStage<?> future) {
74+
protected Span internalSpan(final String path, final String method, final CompletionStage<?> future) {
7675
return SPAN;
7776
}
7877

7978
@Override
80-
public Span span(final String path, final String method) {
79+
protected Span internalSpan(final HttpRequest request, final CompletionStage<?> future) {
8180
return SPAN;
8281
}
83-
84-
@Override
85-
public Span span(final HttpRequest request) {
86-
return SPAN;
87-
}
88-
89-
@Override
90-
public Span span(final HttpRequest request, final CompletionStage<?> future) {
91-
return SPAN;
92-
}
93-
94-
@Override
95-
public void attachSpanToFuture(final Span span, final CompletionStage<?> future) {
96-
requireNonNull(span);
97-
requireNonNull(future);
98-
future.whenComplete(
99-
(result, t) -> {
100-
if (t == null) {
101-
span.success();
102-
} else {
103-
span.failure(t);
104-
}
105-
span.close();
106-
});
107-
}
10882
}

src/test/java/com/spotify/github/http/okhttp/OkHttpHttpClientTest.java

Lines changed: 90 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,43 +20,76 @@
2020

2121
package com.spotify.github.http.okhttp;
2222

23+
import static org.junit.jupiter.api.Assertions.*;
24+
import static org.mockito.ArgumentMatchers.any;
25+
import static org.mockito.Mockito.*;
26+
2327
import com.spotify.github.http.HttpRequest;
2428
import com.spotify.github.http.HttpResponse;
2529
import com.spotify.github.http.ImmutableHttpRequest;
30+
import com.spotify.github.tracing.NoopTracer;
31+
import com.spotify.github.tracing.Span;
32+
import com.spotify.github.tracing.TraceHelper;
2633
import com.spotify.github.tracing.Tracer;
34+
import com.spotify.github.tracing.opencensus.OpenCensusTracer;
35+
import com.spotify.github.tracing.opentelemetry.OpenTelemetryTracer;
36+
import java.io.IOException;
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
import java.util.concurrent.CompletableFuture;
40+
import java.util.concurrent.CompletionException;
41+
import java.util.stream.Stream;
2742
import okhttp3.*;
2843
import org.junit.jupiter.api.AfterEach;
2944
import org.junit.jupiter.api.BeforeAll;
30-
import org.junit.jupiter.api.Test;
45+
import org.junit.jupiter.api.BeforeEach;
46+
import org.junit.jupiter.params.ParameterizedTest;
47+
import org.junit.jupiter.params.provider.MethodSource;
3148
import org.mockito.ArgumentCaptor;
3249

33-
import java.io.IOException;
34-
import java.util.concurrent.CompletableFuture;
35-
import java.util.concurrent.CompletionException;
36-
37-
import static org.junit.jupiter.api.Assertions.*;
38-
import static org.mockito.ArgumentMatchers.any;
39-
import static org.mockito.Mockito.*;
40-
4150
class OkHttpHttpClientTest {
42-
private static OkHttpClient okHttpClient;
51+
private static final OkHttpClient okHttpClient = mock(OkHttpClient.class);
52+
private static final OkHttpClient.Builder mockOkHttpClientBuilder =
53+
mock(OkHttpClient.Builder.class);
54+
private static final Tracer noopTracer = mock(NoopTracer.class);
55+
private static final Tracer ocTracer = mock(OpenCensusTracer.class);
56+
private static final Tracer otTracer = mock(OpenTelemetryTracer.class);
57+
private static final Span mockSpan = mock(Span.class);
58+
private static final Call.Factory mockCallFactory = mock(Call.Factory.class);
59+
4360
private static OkHttpHttpClient httpClient;
44-
private static Tracer tracer;
61+
62+
static Stream<Tracer> tracers() {
63+
return Stream.of(noopTracer, ocTracer, otTracer);
64+
}
4565

4666
@BeforeAll
4767
static void setUp() {
48-
okHttpClient = mock(OkHttpClient.class);
49-
tracer = mock(Tracer.class);
50-
httpClient = new OkHttpHttpClient(okHttpClient, tracer);
68+
httpClient =
69+
new OkHttpHttpClient(okHttpClient, noopTracer) {
70+
@Override
71+
protected Call.Factory createTracedClientOpenTelemetry() {
72+
return mockCallFactory;
73+
}
74+
};
75+
}
76+
77+
@BeforeEach
78+
void setUpEach() {
79+
List<Interceptor> interceptors = new ArrayList<>();
80+
when(okHttpClient.newBuilder()).thenReturn(mockOkHttpClientBuilder);
81+
when(mockOkHttpClientBuilder.networkInterceptors()).thenReturn(interceptors);
82+
when(mockOkHttpClientBuilder.build()).thenReturn(okHttpClient);
5183
}
5284

5385
@AfterEach
5486
void tearDown() {
55-
reset(okHttpClient, tracer);
87+
reset(okHttpClient, noopTracer, ocTracer, otTracer, mockSpan);
5688
}
5789

58-
@Test
59-
void sendSuccessfully() throws IOException {
90+
@ParameterizedTest
91+
@MethodSource("tracers")
92+
void sendSuccessfully(Tracer tracer) throws IOException {
6093
// Given
6194
final Call call = mock(Call.class);
6295
final ArgumentCaptor<Callback> capture = ArgumentCaptor.forClass(Callback.class);
@@ -72,8 +105,12 @@ void sendSuccessfully() throws IOException {
72105

73106
HttpRequest httpRequest = ImmutableHttpRequest.builder().url("https://example.com").build();
74107
when(okHttpClient.newCall(any())).thenReturn(call);
108+
when(mockCallFactory.newCall(any())).thenReturn(call);
109+
110+
when(tracer.span(any())).thenReturn(mockSpan);
75111

76112
// When
113+
httpClient.setTracer(tracer);
77114
CompletableFuture<HttpResponse> futureResponse = httpClient.send(httpRequest);
78115
capture.getValue().onResponse(call, response);
79116
HttpResponse httpResponse = futureResponse.join();
@@ -84,11 +121,19 @@ void sendSuccessfully() throws IOException {
84121
assertEquals(200, httpResponse.statusCode());
85122
assertEquals("foo", httpResponse.statusMessage());
86123
assertTrue(httpResponse.isSuccessful());
87-
verify(tracer, times(1)).span(any(HttpRequest.class));
124+
if (tracer instanceof NoopTracer || tracer instanceof OpenTelemetryTracer) {
125+
verify(tracer, times(1)).span(any(HttpRequest.class));
126+
127+
} else if (tracer instanceof OpenCensusTracer) {
128+
verify(tracer, times(2)).span(any(HttpRequest.class));
129+
verify(mockSpan).addTag(TraceHelper.TraceTags.HTTP_URL, "https://example.com/");
130+
}
131+
verify(mockSpan, times(1)).close();
88132
}
89133

90-
@Test
91-
void sendWithException() {
134+
@ParameterizedTest
135+
@MethodSource("tracers")
136+
void sendWithException(Tracer tracer) {
92137
// Given
93138
final Call call = mock(Call.class);
94139
final ArgumentCaptor<Callback> capture = ArgumentCaptor.forClass(Callback.class);
@@ -97,18 +142,29 @@ void sendWithException() {
97142

98143
HttpRequest httpRequest = ImmutableHttpRequest.builder().url("https://example.com").build();
99144
when(okHttpClient.newCall(any())).thenReturn(call);
145+
when(mockCallFactory.newCall(any())).thenReturn(call);
146+
when(tracer.span(any())).thenReturn(mockSpan);
100147

101148
// When
149+
httpClient.setTracer(tracer);
102150
CompletableFuture<HttpResponse> futureResponse = httpClient.send(httpRequest);
103151
capture.getValue().onFailure(call, exception);
104152

105153
// Then
106154
assertThrows(CompletionException.class, futureResponse::join);
107-
verify(tracer, times(1)).span(any(HttpRequest.class));
155+
if (tracer instanceof NoopTracer || tracer instanceof OpenTelemetryTracer) {
156+
verify(tracer, times(1)).span(any(HttpRequest.class));
157+
158+
} else if (tracer instanceof OpenCensusTracer) {
159+
verify(tracer, times(2)).span(any(HttpRequest.class));
160+
verify(mockSpan).addTag(TraceHelper.TraceTags.HTTP_URL, "https://example.com/");
161+
}
162+
verify(mockSpan, times(1)).close();
108163
}
109164

110-
@Test
111-
void sendWithClientError() throws IOException {
165+
@ParameterizedTest
166+
@MethodSource("tracers")
167+
void sendWithClientError(Tracer tracer) throws IOException {
112168
// Given
113169
final Call call = mock(Call.class);
114170
final ArgumentCaptor<Callback> capture = ArgumentCaptor.forClass(Callback.class);
@@ -125,8 +181,11 @@ void sendWithClientError() throws IOException {
125181

126182
HttpRequest httpRequest = ImmutableHttpRequest.builder().url("https://example.com").build();
127183
when(okHttpClient.newCall(any())).thenReturn(call);
184+
when(mockCallFactory.newCall(any())).thenReturn(call);
185+
when(tracer.span(any())).thenReturn(mockSpan);
128186

129187
// When
188+
httpClient.setTracer(tracer);
130189
CompletableFuture<HttpResponse> futureResponse = httpClient.send(httpRequest);
131190
capture.getValue().onResponse(call, response);
132191
HttpResponse httpResponse = futureResponse.join();
@@ -137,6 +196,13 @@ void sendWithClientError() throws IOException {
137196
assertEquals(404, httpResponse.statusCode());
138197
assertEquals("Not Found", httpResponse.statusMessage());
139198
assertFalse(httpResponse.isSuccessful());
140-
verify(tracer, times(1)).span(any(HttpRequest.class));
199+
if (tracer instanceof NoopTracer || tracer instanceof OpenTelemetryTracer) {
200+
verify(tracer, times(1)).span(any(HttpRequest.class));
201+
202+
} else if (tracer instanceof OpenCensusTracer) {
203+
verify(tracer, times(2)).span(any(HttpRequest.class));
204+
verify(mockSpan).addTag(TraceHelper.TraceTags.HTTP_URL, "https://example.com/");
205+
}
206+
verify(mockSpan, times(1)).close();
141207
}
142208
}

0 commit comments

Comments
 (0)