You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pp...@apache.org on 2022/03/31 09:48:13 UTC

[camel-quarkus] 41/43: Improve MicroProfile Fault Tolerance extension test coverage

This is an automated email from the ASF dual-hosted git repository.

ppalaga pushed a commit to branch 2.7.x
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit 1d3928ccec604e49fed09c0c4f4a6ee4409565e0
Author: James Netherton <ja...@gmail.com>
AuthorDate: Tue Mar 29 14:54:38 2022 +0100

    Improve MicroProfile Fault Tolerance extension test coverage
    
    Fixes #3677
---
 integration-tests/microprofile/pom.xml             | 44 ++++++++--
 .../it/faulttolerance/GreetingBean.java            | 65 +++++++++++++++
 ....java => MicroProfileFaultToleranceHelper.java} | 26 +++---
 .../MicroProfileFaultToleranceRoutes.java          | 93 ++++++++++++++++++++--
 .../MicroprofileFaultToleranceResource.java        | 39 ++++++++-
 .../MicroprofileFaultToleranceTest.java            | 60 +++++++++-----
 6 files changed, 276 insertions(+), 51 deletions(-)

diff --git a/integration-tests/microprofile/pom.xml b/integration-tests/microprofile/pom.xml
index 0cbf0f9..e8984f8 100644
--- a/integration-tests/microprofile/pom.xml
+++ b/integration-tests/microprofile/pom.xml
@@ -33,23 +33,31 @@
     <dependencies>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
-            <artifactId>camel-quarkus-microprofile-fault-tolerance</artifactId>
+            <artifactId>camel-quarkus-bean</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
-            <artifactId>camel-quarkus-microprofile-health</artifactId>
+            <artifactId>camel-quarkus-direct</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
-            <artifactId>camel-quarkus-microprofile-metrics</artifactId>
+            <artifactId>camel-quarkus-log</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
-            <artifactId>camel-quarkus-direct</artifactId>
+            <artifactId>camel-quarkus-microprofile-fault-tolerance</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
-            <artifactId>camel-quarkus-log</artifactId>
+            <artifactId>camel-quarkus-microprofile-health</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-microprofile-metrics</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-mock</artifactId>
         </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
@@ -120,6 +128,19 @@
                 <!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory -->
                 <dependency>
                     <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-bean-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
                     <artifactId>camel-quarkus-direct-deployment</artifactId>
                     <version>${project.version}</version>
                     <type>pom</type>
@@ -183,6 +204,19 @@
                         </exclusion>
                     </exclusions>
                 </dependency>
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-mock-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
             </dependencies>
         </profile>
     </profiles>
diff --git a/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/GreetingBean.java b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/GreetingBean.java
new file mode 100644
index 0000000..3053a27
--- /dev/null
+++ b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/GreetingBean.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.component.microprofile.it.faulttolerance;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
+import org.eclipse.microprofile.faulttolerance.Fallback;
+import org.eclipse.microprofile.faulttolerance.Timeout;
+
+import static org.apache.camel.quarkus.component.microprofile.it.faulttolerance.MicroProfileFaultToleranceRoutes.EXCEPTION_MESSAGE;
+import static org.apache.camel.quarkus.component.microprofile.it.faulttolerance.MicroProfileFaultToleranceRoutes.FALLBACK_RESULT;
+import static org.apache.camel.quarkus.component.microprofile.it.faulttolerance.MicroProfileFaultToleranceRoutes.RESULT;
+
+@ApplicationScoped
+public class GreetingBean {
+
+    @Fallback(fallbackMethod = "fallbackGreeting")
+    public String greetWithFallback() {
+        AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("beanFallback");
+        if (counter.incrementAndGet() == 1) {
+            throw new IllegalStateException(EXCEPTION_MESSAGE);
+        }
+        return RESULT;
+    }
+
+    @Timeout(250)
+    public String greetWithDelay() throws InterruptedException {
+        AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("beanTimeout");
+        if (counter.incrementAndGet() == 1) {
+            Thread.sleep(500);
+            return "Nothing to see here, method invocation timed out!";
+        }
+        return RESULT;
+    }
+
+    @CircuitBreaker(failureRatio = 1.0, requestVolumeThreshold = 1, delay = 0)
+    public String greetWithCircuitBreaker() {
+        AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("beanThreshold");
+        if (counter.incrementAndGet() == 1) {
+            throw new IllegalStateException(EXCEPTION_MESSAGE);
+        }
+        return RESULT;
+    }
+
+    public String fallbackGreeting() {
+        return FALLBACK_RESULT;
+    }
+}
diff --git a/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceResource.java b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroProfileFaultToleranceHelper.java
similarity index 59%
copy from integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceResource.java
copy to integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroProfileFaultToleranceHelper.java
index 348a870..63071b4 100644
--- a/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceResource.java
+++ b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroProfileFaultToleranceHelper.java
@@ -16,25 +16,19 @@
  */
 package org.apache.camel.quarkus.component.microprofile.it.faulttolerance;
 
-import javax.inject.Inject;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
-import org.apache.camel.ProducerTemplate;
+public class MicroProfileFaultToleranceHelper {
 
-@Path("/microprofile-fault-tolerance")
-public class MicroprofileFaultToleranceResource {
+    private static final Map<String, AtomicInteger> COUNTERS = new ConcurrentHashMap();
 
-    @Inject
-    ProducerTemplate producerTemplate;
+    private MicroProfileFaultToleranceHelper() {
+        // Utility class
+    }
 
-    @Path("/route/{route}")
-    @POST
-    @Produces(MediaType.TEXT_PLAIN)
-    public String triggerFaultToleranceRoute(String body, @PathParam("route") String route) {
-        return producerTemplate.requestBody("direct:" + route, body, String.class);
+    public static AtomicInteger getCounter(String name) {
+        return COUNTERS.computeIfAbsent(name, key -> new AtomicInteger());
     }
 }
diff --git a/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroProfileFaultToleranceRoutes.java b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroProfileFaultToleranceRoutes.java
index 3dca95a..4096b1c 100644
--- a/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroProfileFaultToleranceRoutes.java
+++ b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroProfileFaultToleranceRoutes.java
@@ -16,23 +16,47 @@
  */
 package org.apache.camel.quarkus.component.microprofile.it.faulttolerance;
 
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
 import org.apache.camel.builder.RouteBuilder;
+import org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException;
 
+@ApplicationScoped
 public class MicroProfileFaultToleranceRoutes extends RouteBuilder {
 
+    public static final String EXCEPTION_MESSAGE = "Simulated Exception";
     public static final String FALLBACK_RESULT = "Fallback response";
     public static final String RESULT = "Hello Camel Quarkus MicroProfile Fault Tolerance";
-    private static final AtomicInteger COUNTER = new AtomicInteger();
-    private static final AtomicInteger TIMEOUT_COUNTER = new AtomicInteger();
+
+    @Inject
+    GreetingBean greetingBean;
 
     @Override
     public void configure() throws Exception {
-        from("direct:faultTolerance")
+        from("direct:faultToleranceWithBulkhead")
                 .circuitBreaker()
+                .faultToleranceConfiguration().bulkheadEnabled(true).end()
                 .process(exchange -> {
-                    if (COUNTER.incrementAndGet() == 1) {
+                    AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("bulkhead");
+                    if (counter.incrementAndGet() == 1) {
+                        throw new IllegalStateException(EXCEPTION_MESSAGE);
+                    }
+                    exchange.getMessage().setBody(RESULT);
+                })
+                .onFallback()
+                .setBody().constant(FALLBACK_RESULT)
+                .end();
+
+        from("direct:faultToleranceWithFallback")
+                .circuitBreaker()
+                .process(exchange -> {
+                    AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("fallback");
+                    if (counter.incrementAndGet() == 1) {
                         throw new IllegalStateException("Simulated Exception");
                     }
                     exchange.getMessage().setBody(RESULT);
@@ -41,18 +65,73 @@ public class MicroProfileFaultToleranceRoutes extends RouteBuilder {
                 .setBody().constant(FALLBACK_RESULT)
                 .end();
 
+        from("direct:faultToleranceWithThreshold")
+                .circuitBreaker()
+                .faultToleranceConfiguration().failureRatio(100).successThreshold(1).requestVolumeThreshold(1).delay(0).end()
+                .process(exchange -> {
+                    AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("threshold");
+                    if (counter.incrementAndGet() == 1) {
+                        throw new IllegalStateException("Simulated Exception");
+                    }
+                    exchange.getMessage().setBody("Nothing to see here. Circuit breaker is open...");
+                })
+                .end()
+                .setBody().simple(RESULT);
+
         from("direct:faultToleranceWithTimeout")
                 .circuitBreaker()
                 .faultToleranceConfiguration().timeoutEnabled(true).timeoutDuration(500).end()
                 .process(exchange -> {
-                    if (TIMEOUT_COUNTER.incrementAndGet() == 1) {
+                    AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("timeout");
+                    if (counter.incrementAndGet() == 1) {
                         Thread.sleep(1000);
                     }
-                    exchange.getMessage().setBody("Regular hi " + exchange.getMessage().getBody(String.class));
+                    exchange.getMessage().setBody(RESULT);
                 })
                 .onFallback()
-                .setBody().simple("Sorry ${body}, had to fallback!")
+                .setBody().simple(FALLBACK_RESULT)
                 .end();
 
+        // Unavailable on Camel 3.14.2
+        // from("direct:faultToleranceWithTimeoutCustomExecutor")
+        //         .circuitBreaker()
+        //         .faultToleranceConfiguration().timeoutEnabled(true).timeoutScheduledExecutorService("myThreadPool")
+        //         .timeoutDuration(500).end()
+        //         .process(exchange -> {
+        //             AtomicInteger counter = MicroProfileFaultToleranceHelper.getCounter("timeoutCustomExecutor");
+        //             if (counter.incrementAndGet() == 1) {
+        //                 Thread.sleep(1000);
+        //             }
+        //             exchange.getMessage().setBody(RESULT);
+        //         })
+        //         .onFallback()
+        //         .setBody().simple(FALLBACK_RESULT)
+        //         .end();
+
+        from("direct:inheritErrorHandler")
+                .errorHandler(deadLetterChannel("mock:dead").maximumRedeliveries(3).redeliveryDelay(0))
+                .circuitBreaker().inheritErrorHandler(true)
+                .to("mock:start")
+                .throwException(new IllegalArgumentException(EXCEPTION_MESSAGE)).end()
+                .to("mock:end");
+
+        from("direct:circuitBreakerBean")
+                .bean(greetingBean, "greetWithCircuitBreaker");
+
+        from("direct:fallbackBean")
+                .bean(greetingBean, "greetWithFallback");
+
+        from("direct:timeoutBean")
+                .doTry()
+                .bean(greetingBean, "greetWithDelay")
+                .doCatch(TimeoutException.class)
+                .setBody().constant(FALLBACK_RESULT)
+                .end();
+    }
+
+    @Named("myThreadPool")
+    public ScheduledExecutorService myThreadPool() {
+        return getCamelContext().getExecutorServiceManager()
+                .newScheduledThreadPool(this, "myThreadPool", 2);
     }
 }
diff --git a/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceResource.java b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceResource.java
index 348a870..3193d54 100644
--- a/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceResource.java
+++ b/integration-tests/microprofile/src/main/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceResource.java
@@ -23,7 +23,9 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 
+import org.apache.camel.CamelContext;
 import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
 
 @Path("/microprofile-fault-tolerance")
 public class MicroprofileFaultToleranceResource {
@@ -31,10 +33,43 @@ public class MicroprofileFaultToleranceResource {
     @Inject
     ProducerTemplate producerTemplate;
 
+    @Inject
+    CamelContext context;
+
     @Path("/route/{route}")
     @POST
     @Produces(MediaType.TEXT_PLAIN)
-    public String triggerFaultToleranceRoute(String body, @PathParam("route") String route) {
-        return producerTemplate.requestBody("direct:" + route, body, String.class);
+    public String triggerFaultToleranceRoute(@PathParam("route") String route) {
+        return producerTemplate.requestBody("direct:" + route, null, String.class);
+    }
+
+    @Path("/faultToleranceWithThreshold/{route}")
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    public String faultToleranceWithThreshold(@PathParam("route") String route) {
+        try {
+            return producerTemplate.requestBody("direct:" + route, null, String.class);
+        } catch (Exception e) {
+            return e.getCause().getMessage();
+        }
+    }
+
+    @Path("/inheritErrorHandler")
+    @POST
+    public void inheritErrorHandler() throws Exception {
+        MockEndpoint start = context.getEndpoint("mock:start", MockEndpoint.class);
+        start.expectedMessageCount(4);
+
+        MockEndpoint end = context.getEndpoint("mock:end", MockEndpoint.class);
+        end.expectedMessageCount(0);
+
+        MockEndpoint dead = context.getEndpoint("mock:dead", MockEndpoint.class);
+        dead.expectedMessageCount(1);
+
+        producerTemplate.requestBody("direct:inheritErrorHandler", null, String.class);
+
+        start.assertIsSatisfied(5000);
+        end.assertIsSatisfied(5000);
+        dead.assertIsSatisfied(5000);
     }
 }
diff --git a/integration-tests/microprofile/src/test/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceTest.java b/integration-tests/microprofile/src/test/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceTest.java
index ff610c2..4733608 100644
--- a/integration-tests/microprofile/src/test/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceTest.java
+++ b/integration-tests/microprofile/src/test/java/org/apache/camel/quarkus/component/microprofile/it/faulttolerance/MicroprofileFaultToleranceTest.java
@@ -18,46 +18,64 @@ package org.apache.camel.quarkus.component.microprofile.it.faulttolerance;
 
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
-import org.hamcrest.Matchers;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.apache.camel.quarkus.component.microprofile.it.faulttolerance.MicroProfileFaultToleranceRoutes.EXCEPTION_MESSAGE;
+import static org.hamcrest.Matchers.is;
 
 @QuarkusTest
 class MicroprofileFaultToleranceTest {
 
-    @Test
-    public void testCamelMicroProfileFaultToleranceFallback() {
-
+    @ParameterizedTest
+    @MethodSource("routeUris")
+    public void testCamelMicroProfileFaultTolerance(String route) {
         // First request should trigger the fallback response
-        RestAssured.post("/microprofile-fault-tolerance/route/faultTolerance")
+        RestAssured.post("/microprofile-fault-tolerance/route/" + route)
                 .then()
                 .statusCode(200)
-                .body(Matchers.is(MicroProfileFaultToleranceRoutes.FALLBACK_RESULT));
+                .body(is(MicroProfileFaultToleranceRoutes.FALLBACK_RESULT));
 
         // Next request(s) should trigger the expected response
-        RestAssured.post("/microprofile-fault-tolerance/route/faultTolerance")
+        RestAssured.post("/microprofile-fault-tolerance/route/" + route)
                 .then()
                 .statusCode(200)
-                .body(Matchers.is(MicroProfileFaultToleranceRoutes.RESULT));
+                .body(is(MicroProfileFaultToleranceRoutes.RESULT));
     }
 
-    @Test
-    public void testCamelMicroProfileFaultToleranceFallbackWithTimeout() {
-
-        // First request should trigger the fallback response
-        RestAssured.given()
-                .body("Joe")
-                .post("/microprofile-fault-tolerance/route/faultToleranceWithTimeout")
+    @ParameterizedTest
+    @ValueSource(strings = { "faultToleranceWithThreshold", "circuitBreakerBean" })
+    public void testCamelMicroProfileFaultToleranceWithThreshold(String route) {
+        // First request should trigger an exception and open the circuit breaker
+        RestAssured.post("/microprofile-fault-tolerance/faultToleranceWithThreshold/" + route)
                 .then()
                 .statusCode(200)
-                .body(Matchers.is("Sorry Joe, had to fallback!"));
+                .body(is(EXCEPTION_MESSAGE));
 
-        // Next request(s) should trigger the expected response
-        RestAssured.given()
-                .body("Mary")
-                .post("/microprofile-fault-tolerance/route/faultToleranceWithTimeout")
+        // Next request(s) should close the circuit breaker and trigger the expected response
+        RestAssured.post("/microprofile-fault-tolerance/faultToleranceWithThreshold/" + route)
                 .then()
                 .statusCode(200)
-                .body(Matchers.is("Regular hi Mary"));
+                .body(is(MicroProfileFaultToleranceRoutes.RESULT));
+    }
+
+    @Test
+    public void testCamelMicroProfileFaultToleranceInheritErrorHandler() {
+        RestAssured.post("/microprofile-fault-tolerance/inheritErrorHandler")
+                .then()
+                .statusCode(204);
     }
 
+    public static String[] routeUris() {
+        return new String[] {
+                "faultToleranceWithFallback",
+                "faultToleranceWithBulkhead",
+                "faultToleranceWithTimeout",
+                // unavailable on Camel 3.14.2 "faultToleranceWithTimeoutCustomExecutor",
+                "fallbackBean",
+                "timeoutBean",
+        };
+    }
 }