From e7be008914ee39cdda4e904d6c65cb6a15610308 Mon Sep 17 00:00:00 2001 From: Tommy Situ Date: Fri, 19 Jul 2019 15:38:51 +0100 Subject: [PATCH] #213 Add incremental capture option --- .gitignore | 1 + .../hoverfly/junit/core/HoverflyConfig.java | 10 +++ .../core/config/HoverflyConfiguration.java | 10 ++- .../core/config/LocalHoverflyConfig.java | 2 +- .../core/config/RemoteHoverflyConfig.java | 2 +- .../hoverfly/junit/rule/HoverflyRule.java | 6 +- .../junit/core/HoverflyConfigTest.java | 9 ++ .../hoverfly/junit/rule/HoverflyRuleTest.java | 3 +- .../ruletest/IncrementalCaptureTest.java | 85 +++++++++++++++++++ 9 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 src/test/java/io/specto/hoverfly/ruletest/IncrementalCaptureTest.java diff --git a/.gitignore b/.gitignore index 4d993d1e..32ede88f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ target/ # Test resources which shouldn't be part of SCM src/test/resources/hoverfly/recorded-simulation.json +src/test/resources/hoverfly/incremental-capture.json src/test/resources/hoverfly/third-multi-capture/ io_specto_hoverfly_junit5_HoverflyDefaultCaptureTest_NestedTest.json *scenario.json diff --git a/src/main/java/io/specto/hoverfly/junit/core/HoverflyConfig.java b/src/main/java/io/specto/hoverfly/junit/core/HoverflyConfig.java index cbcc5a53..b66b85e6 100644 --- a/src/main/java/io/specto/hoverfly/junit/core/HoverflyConfig.java +++ b/src/main/java/io/specto/hoverfly/junit/core/HoverflyConfig.java @@ -35,6 +35,7 @@ public abstract class HoverflyConfig { protected List captureHeaders; protected boolean webServer; protected boolean statefulCapture; + protected boolean incrementalCapture; protected SimulationPreprocessor simulationPreprocessor; /** @@ -147,6 +148,15 @@ public HoverflyConfig enableStatefulCapture() { return this; } + /** + * By default Hoverfly exports the captured requests and responses to a new file by replacing any existing one. Enable this + * option to import any existing simulation file and append new requests to it in capture mode. + * @return the {@link HoverflyConfig} for further customizations + */ + public HoverflyConfig enableIncrementalCapture() { + this.incrementalCapture = true; + return this; + } /** * Set proxy CA certificate to validate the authenticity of a Hoverfly instance. diff --git a/src/main/java/io/specto/hoverfly/junit/core/config/HoverflyConfiguration.java b/src/main/java/io/specto/hoverfly/junit/core/config/HoverflyConfiguration.java index 09445230..31a0ea74 100644 --- a/src/main/java/io/specto/hoverfly/junit/core/config/HoverflyConfiguration.java +++ b/src/main/java/io/specto/hoverfly/junit/core/config/HoverflyConfiguration.java @@ -36,6 +36,7 @@ public class HoverflyConfiguration { private Logger hoverflyLogger; private LogLevel logLevel; private boolean statefulCapture; + private boolean incrementalCapture; private SimulationPreprocessor simulationPreprocessor; private String binaryNameFormat; private List commands; @@ -55,8 +56,9 @@ public class HoverflyConfiguration { final List captureHeaders, final boolean webServer, final boolean statefulCapture, + final boolean incrementalCapture, final SimulationPreprocessor preprocessor) { - this(proxyPort, adminPort, proxyLocalHost, destination, proxyCaCertificate, captureHeaders, webServer, null, null, statefulCapture, preprocessor); + this(proxyPort, adminPort, proxyLocalHost, destination, proxyCaCertificate, captureHeaders, webServer, null, null, statefulCapture, incrementalCapture, preprocessor); setScheme(scheme); setHost(host); this.authToken = authToken; @@ -77,6 +79,7 @@ public HoverflyConfiguration(final int proxyPort, final Logger hoverflyLogger, final LogLevel logLevel, final boolean statefulCapture, + final boolean incrementalCapture, final SimulationPreprocessor preprocessor ) { this.proxyPort = proxyPort; @@ -89,6 +92,7 @@ public HoverflyConfiguration(final int proxyPort, this.hoverflyLogger = hoverflyLogger; this.logLevel = logLevel; this.statefulCapture = statefulCapture; + this.incrementalCapture = incrementalCapture; this.simulationPreprocessor = preprocessor; } @@ -250,6 +254,10 @@ public boolean isStatefulCapture() { return statefulCapture; } + public boolean isIncrementalCapture() { + return incrementalCapture; + } + private boolean isNotBlank(String str) { return str != null && !str.trim().isEmpty(); } diff --git a/src/main/java/io/specto/hoverfly/junit/core/config/LocalHoverflyConfig.java b/src/main/java/io/specto/hoverfly/junit/core/config/LocalHoverflyConfig.java index b488fa6f..b76709a8 100644 --- a/src/main/java/io/specto/hoverfly/junit/core/config/LocalHoverflyConfig.java +++ b/src/main/java/io/specto/hoverfly/junit/core/config/LocalHoverflyConfig.java @@ -150,7 +150,7 @@ public LocalHoverflyConfig addCommands(String... commands) { @Override public HoverflyConfiguration build() { HoverflyConfiguration configs = new HoverflyConfiguration(proxyPort, adminPort, proxyLocalHost, destination, - proxyCaCert, captureHeaders, webServer, hoverflyLogger, logLevel, statefulCapture, simulationPreprocessor); + proxyCaCert, captureHeaders, webServer, hoverflyLogger, logLevel, statefulCapture, incrementalCapture, simulationPreprocessor); configs.setSslCertificatePath(sslCertificatePath); configs.setSslKeyPath(sslKeyPath); configs.setTlsVerificationDisabled(tlsVerificationDisabled); diff --git a/src/main/java/io/specto/hoverfly/junit/core/config/RemoteHoverflyConfig.java b/src/main/java/io/specto/hoverfly/junit/core/config/RemoteHoverflyConfig.java index e7f49592..33363bda 100644 --- a/src/main/java/io/specto/hoverfly/junit/core/config/RemoteHoverflyConfig.java +++ b/src/main/java/io/specto/hoverfly/junit/core/config/RemoteHoverflyConfig.java @@ -70,7 +70,7 @@ public HoverflyConfiguration build() { proxyPort = DEFAULT_PROXY_PORT; } HoverflyConfiguration configs = new HoverflyConfiguration(scheme, host, proxyPort, adminPort, proxyLocalHost, - destination, proxyCaCert, authToken, adminCertificate, captureHeaders, webServer, statefulCapture, + destination, proxyCaCert, authToken, adminCertificate, captureHeaders, webServer, statefulCapture, incrementalCapture, simulationPreprocessor); HoverflyConfigValidator validator = new HoverflyConfigValidator(); return validator.validate(configs); diff --git a/src/main/java/io/specto/hoverfly/junit/rule/HoverflyRule.java b/src/main/java/io/specto/hoverfly/junit/rule/HoverflyRule.java index 49c82a43..014f022d 100644 --- a/src/main/java/io/specto/hoverfly/junit/rule/HoverflyRule.java +++ b/src/main/java/io/specto/hoverfly/junit/rule/HoverflyRule.java @@ -120,7 +120,7 @@ public static HoverflyRule inCaptureOrSimulationMode(String recordFile) { */ public static HoverflyRule inCaptureOrSimulationMode(String recordFile, HoverflyConfig hoverflyConfig) { Path path = fileRelativeToTestResourcesHoverfly(recordFile); - if (Files.exists(path) && Files.isRegularFile(path)) { + if (Files.isReadable(path)) { return inSimulationMode(file(path), hoverflyConfig); } else { return inCaptureMode(recordFile, hoverflyConfig); @@ -300,6 +300,10 @@ protected void before() { if (hoverflyMode.allowSimulationImport()) { importSimulation(); } + + if (hoverfly.getHoverflyConfig().isIncrementalCapture() && this.capturePath != null && Files.isReadable(this.capturePath)) { + hoverfly.simulate(SimulationSource.file(this.capturePath)); + } } /** diff --git a/src/test/java/io/specto/hoverfly/junit/core/HoverflyConfigTest.java b/src/test/java/io/specto/hoverfly/junit/core/HoverflyConfigTest.java index a7f78e25..59afe70b 100644 --- a/src/test/java/io/specto/hoverfly/junit/core/HoverflyConfigTest.java +++ b/src/test/java/io/specto/hoverfly/junit/core/HoverflyConfigTest.java @@ -39,6 +39,7 @@ public void shouldHaveDefaultSettings() { assertThat(configs.isWebServer()).isFalse(); assertThat(configs.isTlsVerificationDisabled()).isFalse(); assertThat(configs.isStatefulCapture()).isFalse(); + assertThat(configs.isIncrementalCapture()).isFalse(); assertThat(configs.getLogLevel()).isNotPresent(); assertThat(configs.getHoverflyLogger()).isEqualTo(Optional.of(LoggerFactory.getLogger("hoverfly"))); } @@ -171,6 +172,14 @@ public void shouldEnableStatefulCapture() { assertThat(configs.isStatefulCapture()).isTrue(); } + @Test + public void shouldEnableIncrementalCapture() { + HoverflyConfiguration configs = localConfigs().enableIncrementalCapture().build(); + + assertThat(configs.isIncrementalCapture()).isTrue(); + } + + @Test public void shouldAddCommands() { HoverflyConfiguration configs = localConfigs() diff --git a/src/test/java/io/specto/hoverfly/junit/rule/HoverflyRuleTest.java b/src/test/java/io/specto/hoverfly/junit/rule/HoverflyRuleTest.java index 9917d4c0..50e1f4fb 100644 --- a/src/test/java/io/specto/hoverfly/junit/rule/HoverflyRuleTest.java +++ b/src/test/java/io/specto/hoverfly/junit/rule/HoverflyRuleTest.java @@ -3,12 +3,12 @@ import io.specto.hoverfly.junit.core.Hoverfly; import io.specto.hoverfly.junit.core.SimulationSource; -import org.junit.Before; import org.junit.Test; import org.powermock.reflect.Whitebox; import java.nio.file.Path; +import static io.specto.hoverfly.junit.core.HoverflyConfig.localConfigs; import static io.specto.hoverfly.junit.core.SimulationSource.empty; import static io.specto.hoverfly.junit.dsl.HoverflyDsl.service; import static io.specto.hoverfly.junit.dsl.ResponseCreators.success; @@ -116,6 +116,7 @@ public void shouldThrowExceptionIfOutputFilenameIsEmpty() { private Hoverfly getHoverflyMock(HoverflyRule hoverflyRule) { Hoverfly mockHoverfly = mock(Hoverfly.class); Whitebox.setInternalState(hoverflyRule, "hoverfly", mockHoverfly); + when(mockHoverfly.getHoverflyConfig()).thenReturn(localConfigs().build()); return mockHoverfly; } } \ No newline at end of file diff --git a/src/test/java/io/specto/hoverfly/ruletest/IncrementalCaptureTest.java b/src/test/java/io/specto/hoverfly/ruletest/IncrementalCaptureTest.java new file mode 100644 index 00000000..6cf45ef3 --- /dev/null +++ b/src/test/java/io/specto/hoverfly/ruletest/IncrementalCaptureTest.java @@ -0,0 +1,85 @@ +package io.specto.hoverfly.ruletest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.io.Resources; +import io.specto.hoverfly.junit.core.model.RequestResponsePair; +import io.specto.hoverfly.junit.core.model.Simulation; +import io.specto.hoverfly.junit.rule.HoverflyRule; +import io.specto.hoverfly.webserver.CaptureModeTestWebServer; +import org.apache.commons.io.FileUtils; +import org.json.JSONException; +import org.junit.*; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Set; + +import static io.specto.hoverfly.junit.core.HoverflyConfig.localConfigs; +import static java.nio.charset.Charset.defaultCharset; +import static org.assertj.core.api.Assertions.assertThat; + +public class IncrementalCaptureTest { + + + private static final Path SIMULATION_FILE = Paths.get("src/test/resources/hoverfly/incremental-capture.json"); + private static final String SIMULATION_FILE_NAME = "incremental-capture.json"; + + private static URI webServerBaseUrl; + + @BeforeClass + public static void beforeAll() throws Exception { + + Files.deleteIfExists(SIMULATION_FILE); + webServerBaseUrl = CaptureModeTestWebServer.run(); + } + + @Rule + public HoverflyRule hoverflyRule = HoverflyRule.inCaptureMode(SIMULATION_FILE_NAME, + localConfigs() + .captureAllHeaders() + .proxyLocalHost() + .enableIncrementalCapture()); + + + private RestTemplate restTemplate = new RestTemplate(); + + + @Test + public void shouldRecordFirstRequest() { + // When + restTemplate.getForObject(webServerBaseUrl, String.class); + } + + @Test + public void shouldRecordSecondRequest() { + // When + restTemplate.getForObject(webServerBaseUrl + "/other", String.class); + } + + + // We have to assert after the rule has executed because that's when the classpath is written to the filesystem + @AfterClass + public static void afterAll() throws IOException { + + // Verify captured data is expected + final String actualSimulation = new String(Files.readAllBytes(SIMULATION_FILE), defaultCharset()); + + // Verify headers are captured + ObjectMapper objectMapper = new ObjectMapper(); + Simulation simulation = objectMapper.readValue(actualSimulation, Simulation.class); + Set pairs = simulation.getHoverflyData().getPairs(); + assertThat(pairs).hasSize(2); + assertThat(pairs.iterator().next().getRequest().getHeaders()).isNotEmpty(); + + CaptureModeTestWebServer.terminate(); + } + +}