use WireMock for offline tests
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Stefan Kalscheuer 2021-02-28 14:56:50 +01:00
parent 60d94fc5bb
commit 36102326db
2 changed files with 67 additions and 82 deletions

12
pom.xml
View File

@ -129,18 +129,18 @@
<version>3.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.27.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>

View File

@ -16,37 +16,32 @@
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.exception.PermissionDeniedException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicStatusLine;
import org.junit.jupiter.api.*;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import de.stklcode.jvault.connector.exception.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.mockito.MockedStatic;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* JUnit test for HTTP Vault connector.
@ -58,40 +53,33 @@ import static org.mockito.Mockito.*;
class HTTPVaultConnectorOfflineTest {
private static final String INVALID_URL = "foo:/\\1nv4l1d_UrL";
private static MockedStatic<HttpClientBuilder> hcbMock;
private static CloseableHttpClient httpMock;
private final CloseableHttpResponse responseMock = mock(CloseableHttpResponse.class);
private static WireMockServer wireMock;
@BeforeAll
static void prepare() {
// Mock the static HTTPClient creation.
hcbMock = mockStatic(HttpClientBuilder.class);
hcbMock.when(HttpClientBuilder::create).thenReturn(new MockedHttpClientBuilder());
// Initialize HTTP mock.
wireMock = new WireMockServer(WireMockConfiguration.options().dynamicPort());
wireMock.start();
WireMock.configureFor("localhost", wireMock.port());
}
@AfterAll
static void tearDown() {
hcbMock.close();
}
@BeforeEach
void init() {
// Re-initialize HTTP mock to ensure fresh (empty) results.
httpMock = mock(CloseableHttpClient.class);
@AfterAll
static void tearDown() {
wireMock.stop();
wireMock = null;
}
/**
* Test exceptions thrown during request.
*/
@Test
void requestExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("http://127.0.0.1", null, 0, 250);
HTTPVaultConnector connector = new HTTPVaultConnector(wireMock.url("/"), null, 0, 250);
// Test invalid response code.
final int responseCode = 400;
mockResponse(responseCode, "", ContentType.APPLICATION_JSON);
InvalidResponseException e = assertThrows(
mockHttpResponse(responseCode, "", "application/json");
VaultConnectorException e = assertThrows(
InvalidResponseException.class,
connector::getHealth,
"Querying health status succeeded on invalid instance"
@ -101,7 +89,7 @@ class HTTPVaultConnectorOfflineTest {
assertThat("Response message where none was expected", ((InvalidResponseException) e).getResponse(), is(nullValue()));
// Simulate permission denied response.
mockResponse(responseCode, "{\"errors\":[\"permission denied\"]}", ContentType.APPLICATION_JSON);
mockHttpResponse(responseCode, "{\"errors\":[\"permission denied\"]}", "application/json");
assertThrows(
PermissionDeniedException.class,
connector::getHealth,
@ -109,25 +97,27 @@ class HTTPVaultConnectorOfflineTest {
);
// Test exception thrown during request.
when(httpMock.execute(any())).thenThrow(new IOException("Test Exception"));
try (ServerSocket s = new ServerSocket(0)) {
connector = new HTTPVaultConnector("http://localst:" + s.getLocalPort() + "/", null, 0, 250);
}
e = assertThrows(
InvalidResponseException.class,
ConnectionException.class,
connector::getHealth,
"Querying health status succeeded on invalid instance"
);
assertThat("Unexpected exception message", e.getMessage(), is("Unable to read response"));
assertThat("Unexpected exception message", e.getMessage(), is("Unable to connect to Vault server"));
assertThat("Unexpected cause", e.getCause(), instanceOf(IOException.class));
// Now simulate a failing request that succeeds on second try.
connector = new HTTPVaultConnector("https://127.0.0.1", null, 1, 250);
doReturn(responseMock).doReturn(responseMock).when(httpMock).execute(any());
doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, ""))
.when(responseMock).getStatusLine();
when(responseMock.getEntity()).thenReturn(new StringEntity("{}", ContentType.APPLICATION_JSON));
connector = new HTTPVaultConnector(wireMock.url("/"), null, 1, 250);
WireMock.stubFor(
WireMock.any(anyUrl())
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(200).withBody("{}").withHeader("Content-Type", "application/json"))
);
assertDoesNotThrow(connector::getHealth, "Request failed unexpectedly");
}
@ -187,7 +177,7 @@ class HTTPVaultConnectorOfflineTest {
* This test is designed to test exceptions caught and thrown by seal-methods if Vault is not reachable.
*/
@Test
void sealExceptionTest() {
void sealExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector(INVALID_URL);
VaultConnectorException e = assertThrows(
InvalidRequestException.class,
@ -196,21 +186,23 @@ class HTTPVaultConnectorOfflineTest {
);
assertThat("Unexpected exception message", e.getMessage(), is("Invalid URI format"));
// Simulate NULL response (mock not supplied with data).
connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Simulate no connection.
try (ServerSocket s = new ServerSocket(0)) {
connector = new HTTPVaultConnector("http://localst:" + s.getLocalPort() + "/", null, 0, 250);
}
e = assertThrows(
InvalidResponseException.class,
ConnectionException.class,
connector::sealStatus,
"Querying seal status succeeded on invalid instance"
);
assertThat("Unexpected exception message", e.getMessage(), is("Response unavailable"));
assertThat("Unexpected exception message", e.getMessage(), is("Unable to connect to Vault server"));
}
/**
* This test is designed to test exceptions caught and thrown by seal-methods if Vault is not reachable.
*/
@Test
void healthExceptionTest() {
void healthExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector(INVALID_URL);
VaultConnectorException e = assertThrows(
InvalidRequestException.class,
@ -219,14 +211,16 @@ class HTTPVaultConnectorOfflineTest {
);
assertThat("Unexpected exception message", e.getMessage(), is("Invalid URI format"));
// Simulate NULL response (mock not supplied with data).
connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Simulate no connection.
try (ServerSocket s = new ServerSocket(0)) {
connector = new HTTPVaultConnector("http://localhost:" + s.getLocalPort() + "/", null, 0, 250);
}
e = assertThrows(
InvalidResponseException.class,
ConnectionException.class,
connector::getHealth,
"Querying health status succeeded on invalid instance"
);
assertThat("Unexpected exception message", e.getMessage(), is("Response unavailable"));
assertThat("Unexpected exception message", e.getMessage(), is("Unable to connect to Vault server"));
}
/**
@ -234,11 +228,11 @@ class HTTPVaultConnectorOfflineTest {
*/
@Test
void parseExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
HTTPVaultConnector connector = new HTTPVaultConnector(wireMock.url("/"), null, 0, 250);
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
mockResponse(200, "invalid", ContentType.APPLICATION_JSON);
mockHttpResponse(200, "invalid", "application/json");
// Now test the methods.
assertParseError(connector::sealStatus, "sealStatus() succeeded on invalid instance");
@ -267,12 +261,12 @@ class HTTPVaultConnectorOfflineTest {
* Test requests that expect an empty response with code 204, but receive a 200 body.
*/
@Test
void nonEmpty204ResponseTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
void nonEmpty204ResponseTest() {
HTTPVaultConnector connector = new HTTPVaultConnector(wireMock.url("/"), null, 0, 250);
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
mockResponse(200, "{}", ContentType.APPLICATION_JSON);
mockHttpResponse(200, "{}", "application/json");
// Now test the methods expecting a 204.
assertThrows(
@ -361,20 +355,11 @@ class HTTPVaultConnectorOfflineTest {
}
}
private void mockResponse(int status, String body, ContentType type) throws IOException {
when(httpMock.execute(any())).thenReturn(responseMock);
when(responseMock.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), status, ""));
when(responseMock.getEntity()).thenReturn(new StringEntity(body, type));
private void mockHttpResponse(int status, String body, String contentType) {
WireMock.stubFor(
WireMock.any(anyUrl()).willReturn(
aResponse().withStatus(status).withBody(body).withHeader("Content-Type", contentType)
)
);
}
/**
* Mocked {@link HttpClientBuilder} that always returns the mocked client.
*/
private static class MockedHttpClientBuilder extends HttpClientBuilder {
@Override
public CloseableHttpClient build() {
return httpMock;
}
}
}