-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initializing the customThresholds, in CircuitBreakerConfiguration, wi…
…th a blank HashMap in case it's never used. This avoids having an NPE when it's not used.
- Loading branch information
Showing
3 changed files
with
249 additions
and
1 deletion.
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
233 changes: 233 additions & 0 deletions
233
...ircuitbreaker/jersey/CircuitBreakerBundleIntegrationWithoutCustomCircuitBreakersTest.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,233 @@ | ||
package com.github.mtakaki.dropwizard.circuitbreaker.jersey; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.only; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import javax.ws.rs.GET; | ||
import javax.ws.rs.Path; | ||
import javax.ws.rs.client.Client; | ||
import javax.ws.rs.core.Response; | ||
|
||
import org.glassfish.jersey.client.ClientProperties; | ||
import org.junit.Before; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
|
||
import com.codahale.metrics.Meter; | ||
import com.codahale.metrics.MetricRegistry; | ||
import com.codahale.metrics.annotation.ExceptionMetered; | ||
import com.github.mtakaki.dropwizard.circuitbreaker.CircuitBreakerManager; | ||
|
||
import io.dropwizard.Application; | ||
import io.dropwizard.Configuration; | ||
import io.dropwizard.client.JerseyClientBuilder; | ||
import io.dropwizard.client.JerseyClientConfiguration; | ||
import io.dropwizard.setup.Bootstrap; | ||
import io.dropwizard.setup.Environment; | ||
import io.dropwizard.testing.ResourceHelpers; | ||
import io.dropwizard.testing.junit.DropwizardAppRule; | ||
import io.dropwizard.util.Duration; | ||
|
||
import lombok.Getter; | ||
|
||
public class CircuitBreakerBundleIntegrationWithoutCustomCircuitBreakersTest { | ||
private static final String METER_NAME = CircuitBreakerBundleIntegrationWithoutCustomCircuitBreakersTest.TestResource.class | ||
.getTypeName() + ".get.circuitBreaker"; | ||
private static final String OPEN_CIRCUIT_METER_NAME = METER_NAME + ".openCircuit"; | ||
|
||
@Path("/test") | ||
public static class TestResource { | ||
@GET | ||
@CircuitBreaker | ||
public Response get() throws Exception { | ||
throw new Exception("We want this to fail"); | ||
} | ||
|
||
@GET | ||
@Path("/custom") | ||
@CircuitBreaker(name = "customName") | ||
public Response getCustom() throws Exception { | ||
throw new Exception("We want this to fail"); | ||
} | ||
} | ||
|
||
@Getter | ||
public static class TestConfiguration extends Configuration { | ||
private CircuitBreakerConfiguration circuitBreaker; | ||
} | ||
|
||
public static class TestApplication extends Application<TestConfiguration> { | ||
private final CircuitBreakerBundle<TestConfiguration> circuitBreakerBundle = new CircuitBreakerBundle<TestConfiguration>() { | ||
@Override | ||
protected CircuitBreakerConfiguration getConfiguration( | ||
final TestConfiguration configuration) { | ||
return configuration.getCircuitBreaker(); | ||
} | ||
|
||
@Override | ||
protected CircuitBreakerManager buildCircuitBreakerManager( | ||
final Environment environment, | ||
final CircuitBreakerConfiguration circuitBreakerConfiguration) { | ||
// Verifying that our configuration was properly parsed. | ||
assertThat(circuitBreakerConfiguration.getRateType()) | ||
.isSameAs(CircuitBreakerManager.RateType.ONE_MINUTE); | ||
assertThat(circuitBreakerConfiguration.getThreshold()).isEqualTo(0.5d); | ||
|
||
final CircuitBreakerManager circuitBreaker = mock(CircuitBreakerManager.class); | ||
circuitBreakerManager.set(circuitBreaker); | ||
when(circuitBreaker.getDefaultThreshold()).thenReturn(0.5d); | ||
|
||
// Creating the mock Meter that is marked only when there are | ||
// exceptions and the circuit is not open. | ||
final Meter meter = mock(Meter.class); | ||
CircuitBreakerBundleIntegrationWithoutCustomCircuitBreakersTest.meter.set(meter); | ||
|
||
when(circuitBreaker.getMeter(METER_NAME, 0.5d)).thenReturn(meter); | ||
|
||
final Meter customMeter = mock(Meter.class); | ||
CircuitBreakerBundleIntegrationWithoutCustomCircuitBreakersTest.customMeter.set(customMeter); | ||
when(circuitBreaker.getMeter("customName", 0.5d)).thenReturn(customMeter); | ||
|
||
CircuitBreakerBundleIntegrationWithoutCustomCircuitBreakersTest.metricRegistry.set(environment.metrics()); | ||
|
||
return circuitBreaker; | ||
} | ||
}; | ||
|
||
@Override | ||
public void initialize(final Bootstrap<TestConfiguration> bootstrap) { | ||
bootstrap.addBundle(this.circuitBreakerBundle); | ||
}; | ||
|
||
@Override | ||
public void run(final TestConfiguration configuration, final Environment environment) | ||
throws Exception { | ||
environment.jersey().register(new TestResource()); | ||
} | ||
} | ||
|
||
@Rule | ||
public final DropwizardAppRule<TestConfiguration> RULE = new DropwizardAppRule<TestConfiguration>( | ||
TestApplication.class, ResourceHelpers.resourceFilePath("config_without_custom_circuitbreakers.yml")); | ||
|
||
private static Client client; | ||
|
||
public static ThreadLocal<CircuitBreakerManager> circuitBreakerManager = new ThreadLocal<>(); | ||
public static ThreadLocal<Meter> meter = new ThreadLocal<>(); | ||
public static ThreadLocal<Meter> customMeter = new ThreadLocal<>(); | ||
public static ThreadLocal<MetricRegistry> metricRegistry = new ThreadLocal<>(); | ||
|
||
@Before | ||
public void setupClient() { | ||
final JerseyClientConfiguration jerseyClientConfiguration = new JerseyClientConfiguration(); | ||
jerseyClientConfiguration.setConnectionTimeout(Duration.minutes(1L)); | ||
jerseyClientConfiguration.setConnectionRequestTimeout(Duration.minutes(1L)); | ||
jerseyClientConfiguration.setTimeout(Duration.minutes(1L)); | ||
client = new JerseyClientBuilder(this.RULE.getEnvironment()) | ||
.using(jerseyClientConfiguration) | ||
.withProperty(ClientProperties.FOLLOW_REDIRECTS, Boolean.FALSE) | ||
.build("test client"); | ||
} | ||
|
||
/** | ||
* Testing that our meter gets incremented when a request fails. This should | ||
* behave like an {@link ExceptionMetered} annotation. | ||
*/ | ||
@Test | ||
public void testMeterCountIsIncremented() { | ||
when(circuitBreakerManager.get().isCircuitOpen(METER_NAME)).thenReturn(false); | ||
final Meter openCircuitMeter = metricRegistry.get().meter(OPEN_CIRCUIT_METER_NAME); | ||
final long beforeOpenCircuitCount = openCircuitMeter.getCount(); | ||
|
||
// We wanted this request to fail. | ||
this.sendGetRequestAndVerifyStatus("/test", 500); | ||
|
||
// Verifying the meter was called once the exception happened. | ||
verify(meter.get(), only()).mark(); | ||
// The count of our open circuit meter should be the same. | ||
assertThat(openCircuitMeter.getCount()).isEqualTo(beforeOpenCircuitCount); | ||
} | ||
|
||
@Test | ||
public void testCustomMeterCountIsIncremented() { | ||
when(circuitBreakerManager.get().isCircuitOpen("custom")).thenReturn(false); | ||
final Meter openCircuitMeter = metricRegistry.get().meter("custom.openCircuit"); | ||
final long beforeOpenCircuitCount = openCircuitMeter.getCount(); | ||
|
||
// We wanted this request to fail. | ||
this.sendGetRequestAndVerifyStatus("/test/custom", 500); | ||
|
||
// Verifying the meter was called once the exception happened. | ||
verify(customMeter.get(), only()).mark(); | ||
// The count of our open circuit meter should be the same. | ||
assertThat(openCircuitMeter.getCount()).isEqualTo(beforeOpenCircuitCount); | ||
} | ||
|
||
/** | ||
* Testing that when the circuit is open we should get 503 responses. | ||
*/ | ||
@Test | ||
public void testCircuitBreakerIsOpened() { | ||
when(circuitBreakerManager.get().isCircuitOpen(METER_NAME)).thenReturn(true); | ||
final Meter openCircuitMeter = metricRegistry.get().meter(OPEN_CIRCUIT_METER_NAME); | ||
final long beforeOpenCircuitCount = openCircuitMeter.getCount(); | ||
|
||
// We should get 503 - Service unavailable. | ||
this.sendGetRequestAndVerifyStatus("/test", 503); | ||
|
||
// Verifying the meter was not called because the circuit was opened. | ||
verify(meter.get(), times(0)).mark(); | ||
// The count of our open circuit meter should have increased. | ||
assertThat(openCircuitMeter.getCount()).isGreaterThan(beforeOpenCircuitCount); | ||
} | ||
|
||
/** | ||
* Testing that when the circuit is open we should get 503 responses and if | ||
* it closes we should get 500s again. | ||
*/ | ||
@Test | ||
public void testCircuitBreakerIsOpenedAndClosesAgain() { | ||
when(circuitBreakerManager.get().isCircuitOpen(METER_NAME)).thenReturn(true); | ||
final Meter openCircuitMeter = metricRegistry.get().meter(OPEN_CIRCUIT_METER_NAME); | ||
final long beforeOpenCircuitCount = openCircuitMeter.getCount(); | ||
|
||
// We should get 503 - Service unavailable. | ||
this.sendGetRequestAndVerifyStatus("/test", 503); | ||
// The count of our open circuit meter should have increased. | ||
final long afterOpenCircuitCount = openCircuitMeter.getCount(); | ||
assertThat(afterOpenCircuitCount).isGreaterThan(beforeOpenCircuitCount); | ||
|
||
// Verifying the meter was not called because the circuit was opened. | ||
verify(meter.get(), times(0)).mark(); | ||
|
||
when(circuitBreakerManager.get().isCircuitOpen(METER_NAME)).thenReturn(false); | ||
|
||
// We should get 500 again. | ||
this.sendGetRequestAndVerifyStatus("/test", 500); | ||
|
||
// Verifying the meter was called only once as the the first time the | ||
// circuit was opened. | ||
verify(meter.get(), times(1)).mark(); | ||
// The count of our open circuit meter should be the same as | ||
// afterOpenCircuitCount. | ||
assertThat(openCircuitMeter.getCount()).isEqualTo(afterOpenCircuitCount); | ||
} | ||
|
||
/** | ||
* Sends a request and verify it returns the given status code. | ||
* | ||
* @param httpStatus | ||
* The expected status code. | ||
*/ | ||
private void sendGetRequestAndVerifyStatus(final String path, final int httpStatus) { | ||
final Response response = client.target( | ||
String.format("http://localhost:%d%s", this.RULE.getLocalPort(), path)) | ||
.request().get(); | ||
|
||
assertThat(response.getStatus()).isEqualTo(httpStatus); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/test/resources/config_without_custom_circuitbreakers.yml
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,14 @@ | ||
circuitBreaker: | ||
threshold: 0.5 | ||
rateType: ONE_MINUTE | ||
|
||
logging: | ||
level: ERROR | ||
|
||
server: | ||
applicationConnectors: | ||
- type: http | ||
port: 0 | ||
adminConnectors: | ||
- type: http | ||
port: 0 |