/* * * Copyright 2017 Robert Winkler, Lucas Lech * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * */ package io.github.resilience4j.bulkhead; import io.github.resilience4j.test.HelloWorldService; import io.vavr.CheckedConsumer; import io.vavr.CheckedFunction0; import io.vavr.CheckedFunction1; import io.vavr.CheckedRunnable; import io.vavr.control.Try; import org.junit.Before; import org.junit.Test; import org.mockito.BDDMockito; import org.mockito.Mockito; import javax.xml.ws.WebServiceException; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.times; public class BulkheadTest { private HelloWorldService helloWorldService; private BulkheadConfig config; @Before public void setUp(){ helloWorldService = Mockito.mock(HelloWorldService.class); config = BulkheadConfig.custom() .maxConcurrentCalls(1) .build(); } @Test public void shouldDecorateSupplierAndReturnWithSuccess() { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello world"); // When Supplier<String> supplier = Bulkhead.decorateSupplier(bulkhead, helloWorldService::returnHelloWorld); // Then assertThat(supplier.get()).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld(); } @Test public void shouldExecuteSupplierAndReturnWithSuccess() { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello world"); // When String result = bulkhead.executeSupplier(helloWorldService::returnHelloWorld); // Then assertThat(result).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld(); } @Test public void shouldDecorateSupplierAndReturnWithException() { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new RuntimeException("BAM!")); // When Supplier<String> supplier = Bulkhead.decorateSupplier(bulkhead, helloWorldService::returnHelloWorld); Try<String> result = Try.of(supplier::get); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld(); } @Test public void shouldDecorateCheckedSupplierAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world"); // When CheckedFunction0<String> checkedSupplier = Bulkhead.decorateCheckedSupplier(bulkhead, helloWorldService::returnHelloWorldWithException); // Then assertThat(checkedSupplier.apply()).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCheckedSupplierAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willThrow(new RuntimeException("BAM!")); // When CheckedFunction0<String> checkedSupplier = Bulkhead.decorateCheckedSupplier(bulkhead, helloWorldService::returnHelloWorldWithException); Try<String> result = Try.of(checkedSupplier); // Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCallableAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world"); // When Callable<String> callable = Bulkhead.decorateCallable(bulkhead, helloWorldService::returnHelloWorldWithException); // Then assertThat(callable.call()).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorldWithException(); } @Test public void shouldExecuteCallableAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world"); // When String result = bulkhead.executeCallable(helloWorldService::returnHelloWorldWithException); // Then assertThat(result).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCallableAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willThrow(new RuntimeException("BAM!")); // When Callable<String> callable = Bulkhead.decorateCallable(bulkhead, helloWorldService::returnHelloWorldWithException); Try<String> result = Try.of(callable::call); // Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCheckedRunnableAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When Bulkhead.decorateCheckedRunnable(bulkhead, helloWorldService::sayHelloWorldWithException) .run(); // Then assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).sayHelloWorldWithException(); } @Test public void shouldDecorateCheckedRunnableAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When CheckedRunnable checkedRunnable = Bulkhead.decorateCheckedRunnable(bulkhead, () -> {throw new RuntimeException("BAM!");}); Try<Void> result = Try.run(checkedRunnable); // Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldDecorateRunnableAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); //When Bulkhead.decorateRunnable(bulkhead, helloWorldService::sayHelloWorld) .run(); //Then assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).sayHelloWorld(); } @Test public void shouldExecuteRunnableAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When bulkhead.executeRunnable(helloWorldService::sayHelloWorld); // Then assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).sayHelloWorld(); } @Test public void shouldDecorateRunnableAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When Runnable runnable = Bulkhead.decorateRunnable(bulkhead, () -> {throw new RuntimeException("BAM!");}); Try<Void> result = Try.run(runnable::run); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldDecorateConsumerAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When Bulkhead.decorateConsumer(bulkhead, helloWorldService::sayHelloWorldWithName) .accept("Tom"); // Then assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).sayHelloWorldWithName("Tom"); } @Test public void shouldDecorateConsumerAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When Consumer<String> consumer = Bulkhead.decorateConsumer(bulkhead, (value) -> {throw new RuntimeException("BAM!");}); Try<Void> result = Try.run(() -> consumer.accept("Tom")); // Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldDecorateCheckedConsumerAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When Bulkhead.decorateCheckedConsumer(bulkhead, helloWorldService::sayHelloWorldWithNameWithException) .accept("Tom"); // Then assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).sayHelloWorldWithNameWithException("Tom"); } @Test public void shouldDecorateCheckedConsumerAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When CheckedConsumer<String> checkedConsumer = Bulkhead.decorateCheckedConsumer(bulkhead, (value) -> { throw new RuntimeException("BAM!"); }); Try<Void> result = Try.run(() -> checkedConsumer.accept("Tom")); // Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldDecorateFunctionAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithName("Tom")).willReturn("Hello world Tom"); // When Function<String, String> function = Bulkhead.decorateFunction(bulkhead, helloWorldService::returnHelloWorldWithName); // Then assertThat(function.apply("Tom")).isEqualTo("Hello world Tom"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorldWithName("Tom"); } @Test public void shouldDecorateFunctionAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithName("Tom")).willThrow(new RuntimeException("BAM!")); // When Function<String, String> function = Bulkhead.decorateFunction(bulkhead, helloWorldService::returnHelloWorldWithName); Try<String> result = Try.of(() -> function.apply("Tom")); // Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldDecorateCheckedFunctionAndReturnWithSuccess() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithNameWithException("Tom")).willReturn("Hello world Tom"); // When String result = Bulkhead.decorateCheckedFunction(bulkhead, helloWorldService::returnHelloWorldWithNameWithException) .apply("Tom"); // Then assertThat(result).isEqualTo("Hello world Tom"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorldWithNameWithException("Tom"); } @Test public void shouldDecorateCheckedFunctionAndReturnWithException() throws Throwable { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorldWithNameWithException("Tom")).willThrow(new RuntimeException("BAM!")); // When CheckedFunction1<String, String> function = Bulkhead.decorateCheckedFunction(bulkhead, helloWorldService::returnHelloWorldWithNameWithException); Try<String> result = Try.of(() -> function.apply("Tom")); // Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldReturnFailureWithBulkheadFullException() { // tag::bulkheadFullException[] // Given BulkheadConfig config = BulkheadConfig.custom().maxConcurrentCalls(2).build(); Bulkhead bulkhead = Bulkhead.of("test", config); bulkhead.isCallPermitted(); bulkhead.isCallPermitted(); // When CheckedRunnable checkedRunnable = Bulkhead.decorateCheckedRunnable(bulkhead, () -> {throw new RuntimeException("BAM!");}); Try result = Try.run(checkedRunnable); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(BulkheadFullException.class); // end::bulkheadFullException[] } @Test public void shouldReturnFailureWithRuntimeException() { // Given BulkheadConfig config = BulkheadConfig.custom().maxConcurrentCalls(2).build(); Bulkhead bulkhead = Bulkhead.of("test", config); bulkhead.isCallPermitted(); //v When CheckedRunnable checkedRunnable = Bulkhead.decorateCheckedRunnable(bulkhead, () -> {throw new RuntimeException("BAM!");}); Try result = Try.run(checkedRunnable); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldInvokeAsyncApply() throws ExecutionException, InterruptedException { // tag::shouldInvokeAsyncApply[] // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When Supplier<String> decoratedSupplier = Bulkhead.decorateSupplier(bulkhead, () -> "This can be any method which returns: 'Hello"); CompletableFuture<String> future = CompletableFuture.supplyAsync(decoratedSupplier) .thenApply(value -> value + " world'"); String result = future.get(); // Then assertThat(result).isEqualTo("This can be any method which returns: 'Hello world'"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); // end::shouldInvokeAsyncApply[] } @Test public void shouldDecorateCompletionStageAndReturnWithSuccess() throws ExecutionException, InterruptedException { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello"); // When Supplier<CompletionStage<String>> completionStageSupplier = () -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld); Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = Bulkhead.decorateCompletionStage(bulkhead, completionStageSupplier); CompletionStage<String> decoratedCompletionStage = decoratedCompletionStageSupplier .get() .thenApply(value -> value + " world"); // Then assertThat(decoratedCompletionStage.toCompletableFuture().get()).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld(); } @Test public void shouldExecuteCompletionStageAndReturnWithSuccess() throws ExecutionException, InterruptedException { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello"); // When CompletionStage<String> decoratedCompletionStage = bulkhead .executeCompletionStage(() -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld)) .thenApply(value -> value + " world"); // Then assertThat(decoratedCompletionStage.toCompletableFuture().get()).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld(); } @Test public void shouldDecorateCompletionStageAndReturnWithExceptionAtSyncStage() throws ExecutionException, InterruptedException { // Given Bulkhead bulkhead = Bulkhead.of("test", config); // When Supplier<CompletionStage<String>> completionStageSupplier = () -> { throw new WebServiceException("BAM! At sync stage"); }; Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = Bulkhead.decorateCompletionStage(bulkhead, completionStageSupplier); // NOTE: Try.of does not detect a completion stage that has been completed with failure ! Try<CompletionStage<String>> result = Try.of(decoratedCompletionStageSupplier::get); // Then the helloWorldService should be invoked 0 times BDDMockito.then(helloWorldService).should(times(0)).returnHelloWorld(); assertThat(result.isSuccess()).isTrue(); result.get() .exceptionally( error -> { // NOTE: Try.of does not detect a completion stage that has been completed with failure ! assertThat(error).isInstanceOf(WebServiceException.class); return null; } ); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldDecorateCompletionStageAndReturnWithExceptionAtAsyncStage() throws ExecutionException, InterruptedException { // Given Bulkhead bulkhead = Bulkhead.of("test", config); BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new RuntimeException("BAM! At async stage")); // When Supplier<CompletionStage<String>> completionStageSupplier = () -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld); Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = Bulkhead.decorateCompletionStage(bulkhead, completionStageSupplier); CompletionStage<String> decoratedCompletionStage = decoratedCompletionStageSupplier.get(); // Then the helloWorldService should be invoked 1 time assertThatThrownBy(decoratedCompletionStage.toCompletableFuture()::get) .isInstanceOf(ExecutionException.class).hasCause(new RuntimeException("BAM! At async stage")); BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld(); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); } @Test public void shouldChainDecoratedFunctions() throws ExecutionException, InterruptedException { // tag::shouldChainDecoratedFunctions[] // Given Bulkhead bulkhead = Bulkhead.of("test", config); Bulkhead anotherBulkhead = Bulkhead.of("testAnother", config); // When I create a Supplier and a Function which are decorated by different Bulkheads CheckedFunction0<String> decoratedSupplier = Bulkhead.decorateCheckedSupplier(bulkhead, () -> "Hello"); CheckedFunction1<String, String> decoratedFunction = Bulkhead.decorateCheckedFunction(anotherBulkhead, (input) -> input + " world"); // and I chain a function with map Try<String> result = Try.of(decoratedSupplier) .mapTry(decoratedFunction::apply); // Then assertThat(result.isSuccess()).isTrue(); assertThat(result.get()).isEqualTo("Hello world"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); assertThat(anotherBulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); // end::shouldChainDecoratedFunctions[] } @Test public void shouldInvokeMap() { // tag::shouldInvokeMap[] // Given Bulkhead bulkhead = Bulkhead.of("testName", config); // When I decorate my function CheckedFunction0<String> decoratedSupplier = Bulkhead.decorateCheckedSupplier(bulkhead, () -> "This can be any method which returns: 'Hello"); // and chain an other function with map Try<String> result = Try.of(decoratedSupplier) .map(value -> value + " world'"); // Then the Try Monad returns a Success<String>, if all functions ran successfully. assertThat(result.isSuccess()).isTrue(); assertThat(result.get()).isEqualTo("This can be any method which returns: 'Hello world'"); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); // end::shouldInvokeMap[] } }