Skip to content

Commit 815b7c5

Browse files
committed
Adding tests
1 parent 194a0ee commit 815b7c5

File tree

2 files changed

+320
-0
lines changed

2 files changed

+320
-0
lines changed

opamp-client/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
implementation("com.squareup.okhttp3:okhttp")
1616
annotationProcessor("com.google.auto.value:auto-value")
1717
compileOnly("com.google.auto.value:auto-value-annotations")
18+
testImplementation("org.mockito:mockito-inline")
1819
}
1920

2021
val opampReleaseInfo = tasks.register<Download>("opampLastReleaseInfo") {
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
package io.opentelemetry.opamp.client.internal.request.service;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.junit.jupiter.api.Assertions.fail;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.ArgumentMatchers.anyInt;
7+
import static org.mockito.ArgumentMatchers.eq;
8+
import static org.mockito.Mockito.clearInvocations;
9+
import static org.mockito.Mockito.doThrow;
10+
import static org.mockito.Mockito.inOrder;
11+
import static org.mockito.Mockito.mock;
12+
import static org.mockito.Mockito.never;
13+
import static org.mockito.Mockito.verify;
14+
import static org.mockito.Mockito.verifyNoInteractions;
15+
import static org.mockito.Mockito.when;
16+
17+
import io.opentelemetry.opamp.client.internal.connectivity.http.HttpErrorException;
18+
import io.opentelemetry.opamp.client.internal.connectivity.http.HttpSender;
19+
import io.opentelemetry.opamp.client.internal.request.Request;
20+
import io.opentelemetry.opamp.client.internal.request.delay.AcceptsDelaySuggestion;
21+
import io.opentelemetry.opamp.client.internal.request.delay.PeriodicDelay;
22+
import io.opentelemetry.opamp.client.internal.request.delay.PeriodicTaskExecutor;
23+
import io.opentelemetry.opamp.client.internal.response.Response;
24+
import java.io.ByteArrayInputStream;
25+
import java.time.Duration;
26+
import java.util.concurrent.CompletableFuture;
27+
import java.util.concurrent.ExecutionException;
28+
import java.util.function.Supplier;
29+
import opamp.proto.AgentToServer;
30+
import opamp.proto.RetryInfo;
31+
import opamp.proto.ServerErrorResponse;
32+
import opamp.proto.ServerErrorResponseType;
33+
import opamp.proto.ServerToAgent;
34+
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.api.extension.ExtendWith;
37+
import org.mockito.InOrder;
38+
import org.mockito.Mock;
39+
import org.mockito.junit.jupiter.MockitoExtension;
40+
41+
@SuppressWarnings("unchecked")
42+
@ExtendWith(MockitoExtension.class)
43+
class HttpRequestServiceTest {
44+
@Mock private HttpSender requestSender;
45+
@Mock private PeriodicDelay periodicRequestDelay;
46+
@Mock private TestPeriodicRetryDelay periodicRetryDelay;
47+
@Mock private PeriodicTaskExecutor executor;
48+
@Mock private RequestService.Callback callback;
49+
@Mock private Supplier<Request> requestSupplier;
50+
private int requestSize = -1;
51+
private HttpRequestService httpRequestService;
52+
53+
@BeforeEach
54+
void setUp() {
55+
httpRequestService =
56+
new HttpRequestService(requestSender, executor, periodicRequestDelay, periodicRetryDelay);
57+
}
58+
59+
@Test
60+
void verifyStart() {
61+
httpRequestService.start(callback, requestSupplier);
62+
63+
InOrder inOrder = inOrder(periodicRequestDelay, executor);
64+
inOrder.verify(executor).start(httpRequestService);
65+
66+
// Try starting it again:
67+
try {
68+
httpRequestService.start(callback, requestSupplier);
69+
fail();
70+
} catch (IllegalStateException e) {
71+
assertThat(e).hasMessage("RequestDispatcher is already running");
72+
}
73+
}
74+
75+
@Test
76+
void verifyStop() {
77+
httpRequestService.start(callback, requestSupplier);
78+
httpRequestService.stop();
79+
80+
verify(executor).stop();
81+
82+
// Try stopping it again:
83+
clearInvocations(executor);
84+
httpRequestService.stop();
85+
verifyNoInteractions(executor);
86+
}
87+
88+
@Test
89+
void verifyStop_whenNotStarted() {
90+
httpRequestService.stop();
91+
92+
verifyNoInteractions(executor, requestSender, periodicRequestDelay);
93+
}
94+
95+
@Test
96+
void whenTryingToStartAfterStopHasBeenCalled_throwException() {
97+
httpRequestService.start(callback, requestSupplier);
98+
httpRequestService.stop();
99+
try {
100+
httpRequestService.start(callback, requestSupplier);
101+
} catch (IllegalStateException e) {
102+
assertThat(e).hasMessage("RequestDispatcher has been stopped");
103+
}
104+
}
105+
106+
@Test
107+
void verifySendingRequest_happyPath() {
108+
HttpSender.Response httpResponse = mock();
109+
ServerToAgent serverToAgent = new ServerToAgent.Builder().build();
110+
attachServerToAgentMessage(serverToAgent.encodeByteString().toByteArray(), httpResponse);
111+
prepareRequest();
112+
enqueueResponse(httpResponse);
113+
114+
httpRequestService.run();
115+
116+
verify(requestSender).send(any(), eq(requestSize));
117+
verify(callback).onRequestSuccess(Response.create(serverToAgent));
118+
}
119+
120+
@Test
121+
void verifySendingRequest_whenTheresAParsingError() {
122+
HttpSender.Response httpResponse = mock();
123+
attachServerToAgentMessage(new byte[] {1, 2, 3}, httpResponse);
124+
prepareRequest();
125+
enqueueResponse(httpResponse);
126+
127+
httpRequestService.run();
128+
129+
verify(requestSender).send(any(), eq(requestSize));
130+
verify(callback).onRequestFailed(any());
131+
}
132+
133+
@Test
134+
void verifySendingRequest_whenThereIsAnExecutionError()
135+
throws ExecutionException, InterruptedException {
136+
prepareRequest();
137+
CompletableFuture<HttpSender.Response> future = mock();
138+
when(requestSender.send(any(), anyInt())).thenReturn(future);
139+
Exception myException = mock();
140+
doThrow(new ExecutionException(myException)).when(future).get();
141+
142+
httpRequestService.run();
143+
144+
verify(requestSender).send(any(), eq(requestSize));
145+
verify(callback).onRequestFailed(myException);
146+
}
147+
148+
@Test
149+
void verifySendingRequest_whenThereIsAnInterruptedException()
150+
throws ExecutionException, InterruptedException {
151+
prepareRequest();
152+
CompletableFuture<HttpSender.Response> future = mock();
153+
when(requestSender.send(any(), anyInt())).thenReturn(future);
154+
InterruptedException myException = mock();
155+
doThrow(myException).when(future).get();
156+
157+
httpRequestService.run();
158+
159+
verify(requestSender).send(any(), eq(requestSize));
160+
verify(callback).onRequestFailed(myException);
161+
}
162+
163+
@Test
164+
void verifySendingRequest_whenThereIsAGenericHttpError() {
165+
HttpSender.Response response = mock();
166+
when(response.statusCode()).thenReturn(500);
167+
when(response.statusMessage()).thenReturn("Error message");
168+
prepareRequest();
169+
enqueueResponse(response);
170+
171+
httpRequestService.run();
172+
173+
verify(callback).onRequestFailed(new HttpErrorException(500, "Error message"));
174+
verifyNoInteractions(executor);
175+
}
176+
177+
@Test
178+
void verifySendingRequest_whenThereIsATooManyRequestsError() {
179+
HttpSender.Response response = mock();
180+
when(response.statusCode()).thenReturn(429);
181+
when(response.statusMessage()).thenReturn("Error message");
182+
prepareRequest();
183+
enqueueResponse(response);
184+
185+
httpRequestService.run();
186+
187+
verify(callback).onRequestFailed(new HttpErrorException(429, "Error message"));
188+
verify(executor).setPeriodicDelay(periodicRetryDelay);
189+
}
190+
191+
@Test
192+
void verifySendingRequest_whenServerProvidesRetryInfo_usingTheProvidedInfo() {
193+
HttpSender.Response response = mock();
194+
long nanosecondsToWaitForRetry = 1000;
195+
ServerErrorResponse errorResponse =
196+
new ServerErrorResponse.Builder()
197+
.type(ServerErrorResponseType.ServerErrorResponseType_Unavailable)
198+
.retry_info(
199+
new RetryInfo.Builder().retry_after_nanoseconds(nanosecondsToWaitForRetry).build())
200+
.build();
201+
ServerToAgent serverToAgent = new ServerToAgent.Builder().error_response(errorResponse).build();
202+
attachServerToAgentMessage(serverToAgent.encodeByteString().toByteArray(), response);
203+
prepareRequest();
204+
enqueueResponse(response);
205+
206+
httpRequestService.run();
207+
208+
verify(callback).onRequestSuccess(Response.create(serverToAgent));
209+
verify(periodicRetryDelay).suggestDelay(Duration.ofNanos(nanosecondsToWaitForRetry));
210+
verify(executor).setPeriodicDelay(periodicRetryDelay);
211+
}
212+
213+
@Test
214+
void verifySendingRequest_whenServerIsUnavailable() {
215+
HttpSender.Response response = mock();
216+
ServerErrorResponse errorResponse =
217+
new ServerErrorResponse.Builder()
218+
.type(ServerErrorResponseType.ServerErrorResponseType_Unavailable)
219+
.build();
220+
ServerToAgent serverToAgent = new ServerToAgent.Builder().error_response(errorResponse).build();
221+
attachServerToAgentMessage(serverToAgent.encodeByteString().toByteArray(), response);
222+
prepareRequest();
223+
enqueueResponse(response);
224+
225+
httpRequestService.run();
226+
227+
verify(callback).onRequestSuccess(Response.create(serverToAgent));
228+
verify(periodicRetryDelay, never()).suggestDelay(any());
229+
verify(executor).setPeriodicDelay(periodicRetryDelay);
230+
}
231+
232+
@Test
233+
void verifySendingRequest_whenThereIsAServiceUnavailableError() {
234+
HttpSender.Response response = mock();
235+
when(response.statusCode()).thenReturn(503);
236+
when(response.statusMessage()).thenReturn("Error message");
237+
prepareRequest();
238+
enqueueResponse(response);
239+
240+
httpRequestService.run();
241+
242+
verify(callback).onRequestFailed(new HttpErrorException(503, "Error message"));
243+
verify(executor).setPeriodicDelay(periodicRetryDelay);
244+
}
245+
246+
@Test
247+
void verifySendingRequest_duringRegularMode() {
248+
httpRequestService.sendRequest();
249+
250+
verify(executor).executeNow();
251+
}
252+
253+
@Test
254+
void verifySendingRequest_duringRetryMode() {
255+
enableRetryMode();
256+
257+
httpRequestService.sendRequest();
258+
259+
verify(executor, never()).executeNow();
260+
}
261+
262+
@Test
263+
void verifySuccessfulSendingRequest_duringRetryMode() {
264+
enableRetryMode();
265+
HttpSender.Response response = mock();
266+
attachServerToAgentMessage(
267+
new ServerToAgent.Builder().build().encodeByteString().toByteArray(), response);
268+
enqueueResponse(response);
269+
270+
httpRequestService.run();
271+
272+
verify(executor).setPeriodicDelay(periodicRequestDelay);
273+
}
274+
275+
private void enableRetryMode() {
276+
HttpSender.Response response = mock();
277+
when(response.statusCode()).thenReturn(503);
278+
when(response.statusMessage()).thenReturn("Error message");
279+
prepareRequest();
280+
enqueueResponse(response);
281+
282+
httpRequestService.run();
283+
}
284+
285+
private void prepareRequest() {
286+
httpRequestService.start(callback, requestSupplier);
287+
clearInvocations(executor);
288+
AgentToServer agentToServer = new AgentToServer.Builder().sequence_num(10).build();
289+
requestSize = agentToServer.encodeByteString().size();
290+
Request request = Request.create(agentToServer);
291+
when(requestSupplier.get()).thenReturn(request);
292+
}
293+
294+
private void enqueueResponse(HttpSender.Response httpResponse) {
295+
when(requestSender.send(any(), anyInt()))
296+
.thenReturn(CompletableFuture.completedFuture(httpResponse));
297+
}
298+
299+
private static void attachServerToAgentMessage(
300+
byte[] serverToAgent, HttpSender.Response httpResponse) {
301+
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serverToAgent);
302+
when(httpResponse.statusCode()).thenReturn(200);
303+
when(httpResponse.bodyInputStream()).thenReturn(byteArrayInputStream);
304+
}
305+
306+
private static class TestPeriodicRetryDelay implements PeriodicDelay, AcceptsDelaySuggestion {
307+
308+
@Override
309+
public void suggestDelay(Duration delay) {}
310+
311+
@Override
312+
public Duration getNextDelay() {
313+
return null;
314+
}
315+
316+
@Override
317+
public void reset() {}
318+
}
319+
}

0 commit comments

Comments
 (0)