Skip to content

Commit

Permalink
test: HttpRequest and HttpResponse based tests don't require mocks
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <marc@marcnuri.com>
  • Loading branch information
manusa committed Apr 4, 2022
1 parent 48bb1eb commit db396dc
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 147 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public String bodyString() {
return bodyString;
}

public TestHttpRequest withUri(String uri) {
return withUri(URI.create(uri));
}

public TestHttpRequest withUri(URI uri) {
this.uri = uri;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
*/
package io.fabric8.kubernetes.client.http;

import java.nio.charset.StandardCharsets;
import java.util.Optional;

/**
* Basic {@link HttpResponse} implementation to be used in tests instead of mocks or real responses.
*
*
* @param <T> type of the response body.
*/
public class TestHttpResponse<T> extends TestHttpHeaders<TestHttpResponse<T>> implements HttpResponse<T> {
Expand Down Expand Up @@ -53,7 +54,7 @@ public int getCode() {
return code;
}

public HttpResponse<T> withCode(int code) {
public TestHttpResponse<T> withCode(int code) {
this.code = code;
return this;
}
Expand All @@ -62,7 +63,7 @@ public T getBody() {
return body;
}

public HttpResponse<T> withBody(T body) {
public TestHttpResponse<T> withBody(T body) {
this.body = body;
return this;
}
Expand All @@ -80,4 +81,8 @@ public HttpResponse<T> withPreviousResponse(HttpResponse<T> previousResponse) {
this.previousResponse = previousResponse;
return this;
}

public static TestHttpResponse<byte[]> from(int code, String body) {
return new TestHttpResponse<byte[]>().withCode(code).withBody(body.getBytes(StandardCharsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.http.HttpClient;
import io.fabric8.kubernetes.client.http.HttpRequest;
import io.fabric8.kubernetes.client.http.TestHttpResponse;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.io.File;
import java.net.HttpURLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
Expand All @@ -31,7 +31,6 @@
import static io.fabric8.kubernetes.client.Config.KUBERNETES_AUTH_SERVICEACCOUNT_TOKEN_FILE_SYSTEM_PROPERTY;
import static io.fabric8.kubernetes.client.Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY;
import static io.fabric8.kubernetes.client.Config.KUBERNETES_KUBECONFIG_FILE;
import static io.fabric8.kubernetes.client.MockHttpClientUtils.buildResponse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
Expand All @@ -52,7 +51,7 @@ void shouldAutoconfigureAfter401() throws Exception {

// Call
boolean reissue = new TokenRefreshInterceptor(Config.autoConfigure(null), null)
.afterFailure(builder, buildResponse(HttpURLConnection.HTTP_UNAUTHORIZED, "foo")).get();
.afterFailure(builder, new TestHttpResponse<>().withCode(401)).get();
Mockito.verify(builder).setHeader("Authorization", "Bearer token");
assertTrue(reissue);
} finally {
Expand All @@ -78,7 +77,7 @@ void shouldReloadInClusterServiceAccount() throws Exception {

// Write new value to token file to simulate renewal.
Files.write(tokenFile.toPath(), "renewed".getBytes());
boolean reissue = interceptor.afterFailure(builder, buildResponse(HttpURLConnection.HTTP_UNAUTHORIZED, "foo")).get();
boolean reissue = interceptor.afterFailure(builder, new TestHttpResponse<>().withCode(401)).get();

// Make the call and check that renewed token was read at 401 Unauthorized.
Mockito.verify(builder).setHeader("Authorization", "Bearer renewed");
Expand Down Expand Up @@ -115,7 +114,7 @@ void shouldRefreshOIDCToken() throws Exception {
Paths.get(tempFile.getPath()), StandardCopyOption.REPLACE_EXISTING);

TokenRefreshInterceptor interceptor = new TokenRefreshInterceptor(config, Mockito.mock(HttpClient.Factory.class));
boolean reissue = interceptor.afterFailure(builder, buildResponse(HttpURLConnection.HTTP_UNAUTHORIZED, "foo")).get();
boolean reissue = interceptor.afterFailure(builder, new TestHttpResponse<>().withCode(401)).get();

// Make the call and check that renewed token was read at 401 Unauthorized.
Mockito.verify(builder).setHeader("Authorization", "Bearer renewed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
import io.fabric8.kubernetes.client.http.HttpClient;
import io.fabric8.kubernetes.client.http.HttpRequest;
import io.fabric8.kubernetes.client.http.HttpRequest.Builder;
import io.fabric8.kubernetes.client.http.HttpResponse;
import io.fabric8.kubernetes.client.http.TestHttpResponse;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -44,18 +44,18 @@
import static org.mockito.Mockito.when;

class DryRunTest {

private HttpClient mockClient;
private KubernetesClient kubernetesClient;
private List<HttpRequest.Builder> builders = new ArrayList<>();
private List<HttpRequest.Builder> builders;

@BeforeEach
public void setUp() throws IOException {
builders.clear();
builders = new ArrayList<>();
this.mockClient = Mockito.mock(HttpClient.class, Mockito.RETURNS_DEEP_STUBS);
Config config = new ConfigBuilder().withMasterUrl("https://localhost:8443/").build();
HttpResponse<byte[]> mockResponse = MockHttpClientUtils.buildResponse(HttpURLConnection.HTTP_OK, "{}");
when(mockClient.sendAsync(any(), Mockito.eq(byte[].class)))
.thenReturn(CompletableFuture.completedFuture(mockResponse));
.thenReturn(CompletableFuture.completedFuture(TestHttpResponse.from(200, "{}")));
kubernetesClient = new DefaultKubernetesClient(mockClient, config);
Mockito.when(mockClient.newHttpRequestBuilder()).thenAnswer(answer -> {
HttpRequest.Builder result = Mockito.mock(HttpRequest.Builder.class, Mockito.RETURNS_SELF);
Expand All @@ -64,135 +64,116 @@ public void setUp() throws IOException {
});
}

@AfterEach
void tearDown() {
kubernetesClient.close();
kubernetesClient = null;
}

@Test
void testDryRunDisable() throws IOException {
void testDryRunDisable() {
// When
Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun(false).create(getPod("pod1"));

Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun(false).create(withPod("pod1"));
// Then
verify(mockClient).sendAsync(any(), any());
assertRequest("POST", "/api/v1/namespaces/ns1/pods", null);
assertNotNull(pod);
}

@Test
void testDryRunEnable() throws IOException {
void testDryRunEnable() {
// When
Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun(true).create(getPod("pod1"));

Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun(true).create(withPod("pod1"));
// Then
verify(mockClient).sendAsync(any(), any());
assertRequest("POST", "/api/v1/namespaces/ns1/pods", "dryRun=All");
assertNotNull(pod);
}

@Test
void testCreate() throws IOException {
// Given

void testCreate() {
// When
Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun().create(getPod("pod1"));

Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun().create(withPod("pod1"));
// Then
verify(mockClient).sendAsync(any(), any());
assertRequest("POST", "/api/v1/namespaces/ns1/pods", "dryRun=All");
assertNotNull(pod);
}

@Test
void testCreateOrReplace() throws IOException {
// Given

void testCreateOrReplace() {
// When
Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun().createOrReplace(getPod("pod1"));

Pod pod = kubernetesClient.pods().inNamespace("ns1").withName("foo").dryRun().createOrReplace(withPod("pod1"));
// Then
verify(mockClient).sendAsync(any(), any());
assertNotNull(pod);
assertRequest("POST", "/api/v1/namespaces/ns1/pods", "dryRun=All");
}

@Test
void testPatch() throws IOException {
// Given

void testPatch() {
// When
kubernetesClient.pods().inNamespace("ns1").withName("pod1").dryRun().patch(getPod("pod1"));

kubernetesClient.pods().inNamespace("ns1").withName("pod1").dryRun().patch(withPod("pod1"));
// Then
verify(mockClient, times(2)).sendAsync(any(), any());
assertRequest(1, "PATCH", "/api/v1/namespaces/ns1/pods/pod1", "dryRun=All");
}

@Test
void testReplace() throws IOException {
// Given

void testReplace() {
// When
kubernetesClient.pods().inNamespace("ns1").withName("pod1").dryRun().replace(getPod("pod1"));

kubernetesClient.pods().inNamespace("ns1").withName("pod-replace").dryRun().replace(withPod("pod-replace"));
// Then
verify(mockClient, times(2)).sendAsync(any(), any());
assertRequest(1, "PUT", "/api/v1/namespaces/ns1/pods/pod1", "dryRun=All");
assertRequest(1, "PUT", "/api/v1/namespaces/ns1/pods/pod-replace", "dryRun=All");
}

@Test
void testDelete() throws IOException {
// Given

void testDelete() {
// When
kubernetesClient.pods().inNamespace("ns1").withName("pod1").dryRun().withPropagationPolicy(DeletionPropagation.BACKGROUND)
.delete();

// Then
verify(mockClient).sendAsync(any(), any());
assertRequest("DELETE", "/api/v1/namespaces/ns1/pods/pod1", "dryRun=All");
}

@Test
void testResourceCreateOrReplace() throws IOException {
// Given

void testResourceCreateOrReplace() {
// When
kubernetesClient.resource(getPod("pod1")).inNamespace("ns1").dryRun().createOrReplace();

kubernetesClient.resource(withPod("pod1")).inNamespace("ns1").dryRun().createOrReplace();
// Then
verify(mockClient).sendAsync(any(), any());
assertRequest("POST", "/api/v1/namespaces/ns1/pods", "dryRun=All");
}

@Test
void testResourceDelete() throws IOException {
// Given

void testResourceDelete() {
// When
kubernetesClient.resource(getPod("pod1")).inNamespace("ns1").dryRun().withPropagationPolicy(DeletionPropagation.BACKGROUND)
kubernetesClient.resource(withPod("pod1")).inNamespace("ns1").dryRun().withPropagationPolicy(DeletionPropagation.BACKGROUND)
.delete();

// Then
verify(mockClient).sendAsync(any(), any());
assertRequest("DELETE", "/api/v1/namespaces/ns1/pods/pod1", "dryRun=All");
}

@Test
void testResourceListCreateOrReplace() throws IOException {
void testResourceListCreateOrReplace() {
// Given
Pod pod = getPod("pod1");
Pod pod = withPod("pod1");
Service svc = new ServiceBuilder().withNewMetadata().withName("svc1").endMetadata().build();

// When
kubernetesClient.resourceList(pod, svc).inNamespace("ns1").dryRun().createOrReplace();

// Then
verify(mockClient, times(2)).sendAsync(any(), any());
assertRequest("POST", "/api/v1/namespaces/ns1/pods", "dryRun=All");
assertRequest(1, "POST", "/api/v1/namespaces/ns1/services", "dryRun=All");
}

@Test
void testResourceListDelete() throws IOException {
void testResourceListDelete() {
// Given
Pod pod = getPod("pod1");
Pod pod = withPod("pod1");
Service svc = new ServiceBuilder().withNewMetadata().withName("svc1").endMetadata().build();

// When
Expand All @@ -205,7 +186,7 @@ void testResourceListDelete() throws IOException {
assertRequest(1, "DELETE", "/api/v1/namespaces/ns1/services/svc1", "dryRun=All");
}

private Pod getPod(String name) {
private Pod withPod(String name) {
return new PodBuilder().withNewMetadata().withName(name).endMetadata().build();
}

Expand Down

0 comments on commit db396dc

Please sign in to comment.