-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add unit tests to core project (#81)
* Update gradle.yml * test: add test branch * feature: add build info * feature: add nativeClient tests * feature: add coverage * feature: initialize client using the builder * feature: add additional tests to nativeclient * feature: add requestUtil tests * feature: add permissions for pr * feature: remove code coverage * feature: remove unneeded deps * feature: remove explicit permissions * feature: report to workflow summary * feature: Update .github/workflows/gradle.yml * feature: test new version * feature: add summary instead of pr comment
- Loading branch information
1 parent
894a5e1
commit 1b35c40
Showing
11 changed files
with
306 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
219 changes: 219 additions & 0 deletions
219
core/client-native/src/test/java/com/thousandeyes/sdk/client/NativeApiClientTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
package com.thousandeyes.sdk.client; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.argThat; | ||
import static org.mockito.Mockito.doReturn; | ||
import static org.mockito.Mockito.doThrow; | ||
import static org.mockito.Mockito.verify; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.net.http.HttpClient; | ||
import java.net.http.HttpHeaders; | ||
import java.net.http.HttpResponse; | ||
import java.time.Duration; | ||
import java.time.OffsetDateTime; | ||
import java.time.ZoneId; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Stream; | ||
|
||
import org.apache.commons.lang3.tuple.Pair; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
|
||
|
||
@ExtendWith(MockitoExtension.class) | ||
class NativeApiClientTest { | ||
@Mock | ||
private HttpResponse<InputStream> httpResponse; | ||
@Mock | ||
private HttpClient httpClient; | ||
@Mock | ||
private HttpClient.Builder httpClientBuilder; | ||
private final ObjectMapper objectMapper = new ObjectMapper() | ||
.findAndRegisterModules(); | ||
private ApiClient apiClient; | ||
|
||
public static Stream<Arguments> invalidRequestProvider() { | ||
return Stream.of( | ||
Arguments.of(IOException.class), | ||
Arguments.of(InterruptedException.class) | ||
); | ||
} | ||
|
||
@BeforeEach | ||
public void setUp() { | ||
doReturn(httpClient) | ||
.when(httpClientBuilder) | ||
.build(); | ||
|
||
apiClient = NativeApiClient.builder() | ||
.baseUri("http://localhost") | ||
.httpClientBuilder(httpClientBuilder) | ||
.mapper(objectMapper) | ||
.build(); | ||
} | ||
|
||
public static Stream<Arguments> validRequestProvider() { | ||
return Stream.of( | ||
Arguments.of("GET with query params and headers", | ||
baseRequestBuilder() | ||
.queryParams(List.of(Pair.of("aid", "33"))) | ||
.build()), | ||
Arguments.of("POST with body and headers", | ||
baseRequestBuilder() | ||
.method("POST") | ||
.header("content-type", List.of("application/json")) | ||
.queryParams(List.of(Pair.of("aid", "33"))) | ||
.requestBody(new Request("name", 1)) | ||
.build()), | ||
Arguments.of("POST with string body", | ||
baseRequestBuilder() | ||
.method("POST") | ||
.requestBody("a String") | ||
.build()), | ||
Arguments.of("DELETE without query params, body and headers", | ||
baseRequestBuilder() | ||
.method("DELETE") | ||
.build()) | ||
); | ||
} | ||
|
||
@ParameterizedTest(name = "{0}") | ||
@MethodSource("validRequestProvider") | ||
void shouldSendRequestCorrectly(String testName, ApiRequest request) | ||
throws ApiException, IOException, InterruptedException | ||
{ | ||
var expectedResponse = new Response("name", OffsetDateTime.now(ZoneId.of("UTC"))); | ||
stubHttpClient(expectedResponse); | ||
|
||
var response = apiClient.send(request, Response.class); | ||
|
||
assertEquals(expectedResponse, response.getData()); | ||
} | ||
|
||
@ParameterizedTest(name = "{0}") | ||
@MethodSource("validRequestProvider") | ||
void shouldSendRequestForListCorrectly(String testName, ApiRequest request) | ||
throws ApiException, IOException, InterruptedException | ||
{ | ||
var expectedResponse = List.of(new Response("name", OffsetDateTime.now(ZoneId.of("UTC")))); | ||
stubHttpClient(expectedResponse); | ||
|
||
var response = apiClient.sendForList(request, Response.class); | ||
|
||
assertEquals(expectedResponse, response.getData()); | ||
} | ||
|
||
@ParameterizedTest(name = "{0}") | ||
@MethodSource("invalidRequestProvider") | ||
void shouldThrowWhenHttpClientThrows(Class<Throwable> expectedException) | ||
throws IOException, InterruptedException | ||
{ | ||
var request = baseRequestBuilder().build(); | ||
stubHttpClient(expectedException); | ||
|
||
var exception = assertThrows(ApiException.class, | ||
() -> apiClient.send(request, Response.class)); | ||
|
||
assertEquals(expectedException, exception.getCause().getClass()); | ||
} | ||
|
||
@ParameterizedTest(name = "{0}") | ||
@ValueSource(ints = { 400, 401, 403, 404, 500, 502, 503 }) | ||
void shouldThrowWhenHttpClientResponseNot2xx(int statusCode) | ||
throws IOException, InterruptedException | ||
{ | ||
var request = baseRequestBuilder().build(); | ||
var expectedResponse = List.of(new Response("name", OffsetDateTime.now(ZoneId.of("UTC")))); | ||
stubHttpClient(expectedResponse, statusCode); | ||
|
||
var exception = assertThrows(ApiException.class, | ||
() -> apiClient.send(request, Response.class)); | ||
|
||
assertEquals(statusCode, exception.getCode()); | ||
} | ||
|
||
@Test | ||
public void shouldAllowDifferentApiClientInstancesWithDifferentBearerTokens() | ||
throws ApiException, IOException, InterruptedException | ||
{ | ||
sendRequestAndVerifyBearerToken("BearerTokenA"); | ||
sendRequestAndVerifyBearerToken("BearerTokenB"); | ||
} | ||
|
||
private void sendRequestAndVerifyBearerToken(String bearerToken) | ||
throws IOException, InterruptedException, ApiException | ||
{ | ||
stubHttpClient(new Response("name", OffsetDateTime.now(ZoneId.of("UTC")))); | ||
var apiClient = NativeApiClient.builder() | ||
.baseUri("http://localhost") | ||
.bearerToken(bearerToken) | ||
.httpClientBuilder(httpClientBuilder) | ||
.mapper(objectMapper) | ||
.build(); | ||
|
||
apiClient.send(baseRequestBuilder().build(), Response.class); | ||
|
||
verify(httpClient).send(argThat(request -> request.headers() | ||
.firstValue("Authorization") | ||
.orElse("") | ||
.equals("Bearer " + bearerToken)), | ||
any()); | ||
} | ||
|
||
private void stubHttpClient(Class<Throwable> expectedException) throws IOException, | ||
InterruptedException | ||
{ | ||
doThrow(expectedException) | ||
.when(httpClient) | ||
.send(any(), any()); | ||
} | ||
|
||
private <T> void stubHttpClient(T expectedResponse) throws IOException, | ||
InterruptedException | ||
{ | ||
stubHttpClient(expectedResponse, 200); | ||
} | ||
|
||
private <T> void stubHttpClient(T expectedResponse, int statusCode) throws IOException, | ||
InterruptedException | ||
{ | ||
var expectedResponseBytes = objectMapper.writeValueAsBytes(expectedResponse); | ||
var body = new ByteArrayInputStream(expectedResponseBytes); | ||
doReturn(body).when(httpResponse).body(); | ||
var headers = HttpHeaders.of(Map.of(), (s, s2) -> true); | ||
doReturn(headers).when(httpResponse).headers(); | ||
doReturn(statusCode).when(httpResponse).statusCode(); | ||
doReturn(httpResponse).when(httpClient).send(any(), any()); | ||
} | ||
|
||
private static ApiRequest.ApiRequestBuilder baseRequestBuilder() { | ||
return ApiRequest.builder() | ||
.method("GET") | ||
.path("/account-groups") | ||
.readTimeout(Duration.ofSeconds(2)); | ||
} | ||
|
||
private record Request(String name, Integer interval) { | ||
} | ||
|
||
|
||
|
||
private record Response(String name, OffsetDateTime dateTime) { | ||
} | ||
} |
Oops, something went wrong.