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

Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 6ec13d5d4e66 CAMEL-24473: Fix Kafka async consumer readiness health 
check (#25844)
6ec13d5d4e66 is described below

commit 6ec13d5d4e66a7599ab598e3284392b4653869b7
Author: Omar Atie <[email protected]>
AuthorDate: Fri Aug 28 06:28:22 2026 -0700

    CAMEL-24473: Fix Kafka async consumer readiness health check (#25844)
    
    * CAMEL-24473: Fix Kafka async consumer readiness health check
    
    Resolve network readiness for AsyncKafkaConsumer (group.protocol=consumer)
    via NetworkClientDelegate instead of failing open when ClassicKafkaConsumer
    reflection path is unavailable. Fail closed when connectivity cannot be 
verified.
    
    Adds KafkaNetworkHealthHelper, unit tests, and group-protocol health check 
IT.
    
    Co-authored-by: Cursor Agent <[email protected]>
    
    * CAMEL-24473: Address review feedback on Kafka health check helper
    
    Preserve fail-open behavior for custom Kafka clients and producers.
    Add null guards on async reflection chain and real-client unit tests.
    
    Co-authored-by: Cursor Agent <[email protected]>
    
    ---------
    
    Co-authored-by: Cursor Agent <[email protected]>
    Co-authored-by: Cursor Agent <[email protected]>
---
 .../camel/component/kafka/KafkaFetchRecords.java   |  26 +---
 .../component/kafka/KafkaNetworkHealthHelper.java  | 140 +++++++++++++++++++++
 .../camel/component/kafka/KafkaProducer.java       |  26 +---
 .../kafka/KafkaNetworkHealthHelperTest.java        |  88 +++++++++++++
 .../KafkaConsumerGroupProtocolHealthCheckIT.java   | 138 ++++++++++++++++++++
 5 files changed, 368 insertions(+), 50 deletions(-)

diff --git 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaFetchRecords.java
 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaFetchRecords.java
index d371189a8025..40455caddfc0 100644
--- 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaFetchRecords.java
+++ 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaFetchRecords.java
@@ -56,7 +56,6 @@ import org.apache.kafka.clients.consumer.Consumer;
 import org.apache.kafka.clients.consumer.ConsumerRebalanceListener;
 import org.apache.kafka.clients.consumer.ConsumerRecords;
 import org.apache.kafka.clients.consumer.OffsetAndMetadata;
-import org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient;
 import org.apache.kafka.common.TopicPartition;
 import org.apache.kafka.common.errors.InterruptException;
 import org.apache.kafka.common.errors.WakeupException;
@@ -569,30 +568,7 @@ public class KafkaFetchRecords implements Runnable {
         if (!connected) {
             return false;
         }
-
-        boolean ready = true;
-        try {
-            if (consumer instanceof 
org.apache.kafka.clients.consumer.KafkaConsumer) {
-                // need to use reflection to access the network client which 
has API to check if the client has ready
-                // connections
-                org.apache.kafka.clients.consumer.KafkaConsumer kc = 
(org.apache.kafka.clients.consumer.KafkaConsumer) consumer;
-                Object client = 
ReflectionHelper.getField(kc.getClass().getDeclaredField("delegate"), kc);
-                if (client != null) {
-                    ConsumerNetworkClient nc
-                            = (ConsumerNetworkClient) 
ReflectionHelper.getField(client.getClass().getDeclaredField("client"),
-                                    client);
-                    LOG.trace(
-                            "Health-Check calling 
org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient.hasReadyNode");
-                    ready = nc.hasReadyNodes(System.currentTimeMillis());
-                }
-            }
-        } catch (Exception e) {
-            // ignore
-            LOG.debug("Cannot check hasReadyNodes on KafkaConsumer client 
(ConsumerNetworkClient) due to: "
-                      + e.getMessage() + ". This exception is ignored.",
-                    e);
-        }
-        return ready;
+        return KafkaNetworkHealthHelper.consumerHasReadyNodes(consumer);
     }
 
     private Properties getKafkaProps() {
diff --git 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaNetworkHealthHelper.java
 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaNetworkHealthHelper.java
new file mode 100644
index 000000000000..87f63dc81626
--- /dev/null
+++ 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaNetworkHealthHelper.java
@@ -0,0 +1,140 @@
+/*
+ * 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.component.kafka;
+
+import org.apache.camel.util.ReflectionHelper;
+import org.apache.kafka.clients.KafkaClient;
+import org.apache.kafka.clients.NetworkClient;
+import org.apache.kafka.clients.consumer.Consumer;
+import org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient;
+import org.apache.kafka.clients.producer.internals.Sender;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Resolves Kafka client network readiness across classic and async consumer 
protocols.
+ * <p/>
+ * Uses reflection against kafka-clients internals (same approach as 
CAMEL-20592) because the public consumer/producer
+ * APIs do not expose connectivity state.
+ */
+final class KafkaNetworkHealthHelper {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(KafkaNetworkHealthHelper.class);
+
+    private KafkaNetworkHealthHelper() {
+    }
+
+    static boolean consumerHasReadyNodes(Consumer<?, ?> consumer) {
+        if (!(consumer instanceof 
org.apache.kafka.clients.consumer.KafkaConsumer<?, ?> kafkaConsumer)) {
+            // Custom consumer implementations keep legacy fail-open behavior
+            return true;
+        }
+        try {
+            Object delegate = 
ReflectionHelper.getField(kafkaConsumer.getClass().getDeclaredField("delegate"),
 kafkaConsumer);
+            if (delegate == null) {
+                LOG.warn("KafkaConsumer delegate is null; treating consumer 
readiness as not ready");
+                return false;
+            }
+            long now = System.currentTimeMillis();
+
+            Boolean classicReady = classicConsumerHasReadyNodes(delegate, now);
+            if (classicReady != null) {
+                return classicReady;
+            }
+
+            Boolean asyncReady = asyncConsumerHasReadyNodes(delegate, now);
+            if (asyncReady != null) {
+                return asyncReady;
+            }
+
+            LOG.warn("Cannot resolve Kafka consumer network client for 
readiness check; treating as not ready");
+            return false;
+        } catch (Exception e) {
+            LOG.debug("Cannot check hasReadyNodes on KafkaConsumer due to: {}. 
Treating as not ready.", e.getMessage(), e);
+            return false;
+        }
+    }
+
+    static boolean 
producerHasReadyNodes(org.apache.kafka.clients.producer.Producer<?, ?> 
producer) {
+        if (!(producer instanceof 
org.apache.kafka.clients.producer.KafkaProducer<?, ?> kafkaProducer)) {
+            // Custom producer implementations keep legacy fail-open behavior
+            return true;
+        }
+        try {
+            Sender sender
+                    = (Sender) 
ReflectionHelper.getField(kafkaProducer.getClass().getDeclaredField("sender"), 
kafkaProducer);
+            if (sender == null) {
+                return true;
+            }
+            NetworkClient networkClient
+                    = (NetworkClient) 
ReflectionHelper.getField(sender.getClass().getDeclaredField("client"), sender);
+            if (networkClient == null) {
+                return true;
+            }
+            LOG.trace("Health-Check calling NetworkClient.hasReadyNodes");
+            return networkClient.hasReadyNodes(System.currentTimeMillis());
+        } catch (Exception e) {
+            LOG.debug("Cannot check hasReadyNodes on KafkaProducer due to: {}. 
This exception is ignored.", e.getMessage(), e);
+            return true;
+        }
+    }
+
+    private static Boolean classicConsumerHasReadyNodes(Object delegate, long 
now) throws Exception {
+        try {
+            Object client = 
ReflectionHelper.getField(delegate.getClass().getDeclaredField("client"), 
delegate);
+            if (client instanceof ConsumerNetworkClient networkClient) {
+                LOG.trace("Health-Check calling 
ConsumerNetworkClient.hasReadyNodes");
+                return networkClient.hasReadyNodes(now);
+            }
+            if (client instanceof KafkaClient kafkaClient) {
+                return kafkaClient.hasReadyNodes(now);
+            }
+        } catch (NoSuchFieldException e) {
+            // not classic layout
+        }
+        return null;
+    }
+
+    private static Boolean asyncConsumerHasReadyNodes(Object delegate, long 
now) throws Exception {
+        try {
+            Object applicationEventHandler
+                    = 
ReflectionHelper.getField(delegate.getClass().getDeclaredField("applicationEventHandler"),
 delegate);
+            if (applicationEventHandler == null) {
+                return null;
+            }
+            Object networkThread = ReflectionHelper.getField(
+                    
applicationEventHandler.getClass().getDeclaredField("networkThread"), 
applicationEventHandler);
+            if (networkThread == null) {
+                return false;
+            }
+            Object networkClientDelegate = ReflectionHelper.getField(
+                    
networkThread.getClass().getDeclaredField("networkClientDelegate"), 
networkThread);
+            if (networkClientDelegate == null) {
+                return false;
+            }
+            KafkaClient kafkaClient = (KafkaClient) ReflectionHelper.getField(
+                    
networkClientDelegate.getClass().getDeclaredField("client"), 
networkClientDelegate);
+            if (kafkaClient == null) {
+                return false;
+            }
+            LOG.trace("Health-Check calling KafkaClient.hasReadyNodes on async 
consumer delegate");
+            return kafkaClient.hasReadyNodes(now);
+        } catch (NoSuchFieldException e) {
+            return null;
+        }
+    }
+}
diff --git 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
index 7c8c7d722e66..7334c4b93756 100755
--- 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
+++ 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
@@ -50,12 +50,10 @@ import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.ReflectionHelper;
 import org.apache.camel.util.URISupport;
 import org.apache.kafka.clients.CommonClientConfigs;
-import org.apache.kafka.clients.NetworkClient;
 import org.apache.kafka.clients.producer.Producer;
 import org.apache.kafka.clients.producer.ProducerConfig;
 import org.apache.kafka.clients.producer.ProducerRecord;
 import org.apache.kafka.clients.producer.RecordMetadata;
-import org.apache.kafka.clients.producer.internals.Sender;
 import org.apache.kafka.common.header.Header;
 import org.apache.kafka.common.header.internals.RecordHeader;
 import org.slf4j.Logger;
@@ -111,29 +109,7 @@ public class KafkaProducer extends DefaultAsyncProducer 
implements RouteIdAware
     }
 
     public boolean isReady() {
-        boolean ready = true;
-        try {
-            if (kafkaProducer instanceof 
org.apache.kafka.clients.producer.KafkaProducer) {
-                // need to use reflection to access the network client which 
has API to check if the client has ready
-                // connections
-                org.apache.kafka.clients.producer.KafkaProducer kp
-                        = (org.apache.kafka.clients.producer.KafkaProducer) 
kafkaProducer;
-                Sender sender
-                        = (Sender) ReflectionHelper
-                                
.getField(kp.getClass().getDeclaredField("sender"), kp);
-                NetworkClient nc
-                        = (NetworkClient) 
ReflectionHelper.getField(sender.getClass().getDeclaredField("client"), sender);
-                LOG.trace(
-                        "Health-Check calling 
org.apache.kafka.clients.NetworkClient.hasReadyNode");
-                ready = nc.hasReadyNodes(System.currentTimeMillis());
-            }
-        } catch (Exception e) {
-            // ignore
-            LOG.debug("Cannot check hasReadyNodes on KafkaProducer client 
(NetworkClient) due to "
-                      + e.getMessage() + ". This exception is ignored.",
-                    e);
-        }
-        return ready;
+        return KafkaNetworkHealthHelper.producerHasReadyNodes(kafkaProducer);
     }
 
     @SuppressWarnings("rawtypes")
diff --git 
a/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaNetworkHealthHelperTest.java
 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaNetworkHealthHelperTest.java
new file mode 100644
index 000000000000..52ba941da1b8
--- /dev/null
+++ 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaNetworkHealthHelperTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.component.kafka;
+
+import java.util.Properties;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+
+class KafkaNetworkHealthHelperTest {
+
+    @Test
+    void consumerHasReadyNodesShouldFailOpenForCustomConsumer() {
+        
assertTrue(KafkaNetworkHealthHelper.consumerHasReadyNodes(mock(org.apache.kafka.clients.consumer.Consumer.class)));
+    }
+
+    @Test
+    void consumerHasReadyNodesShouldFailOpenForNullConsumer() {
+        assertTrue(KafkaNetworkHealthHelper.consumerHasReadyNodes(null));
+    }
+
+    @Test
+    void producerHasReadyNodesShouldFailOpenForCustomProducer() {
+        
assertTrue(KafkaNetworkHealthHelper.producerHasReadyNodes(mock(org.apache.kafka.clients.producer.Producer.class)));
+    }
+
+    @Test
+    void producerHasReadyNodesShouldFailOpenForNullProducer() {
+        assertTrue(KafkaNetworkHealthHelper.producerHasReadyNodes(null));
+    }
+
+    @Test
+    void 
consumerHasReadyNodesShouldResolveClassicAndAsyncLayoutsWithoutException() {
+        Properties classicProps = consumerProps("classic");
+        Properties asyncProps = consumerProps("consumer");
+
+        try (KafkaConsumer<String, String> classic = new 
KafkaConsumer<>(classicProps);
+             KafkaConsumer<String, String> async = new 
KafkaConsumer<>(asyncProps)) {
+            assertDoesNotThrow(() -> 
KafkaNetworkHealthHelper.consumerHasReadyNodes(classic));
+            assertDoesNotThrow(() -> 
KafkaNetworkHealthHelper.consumerHasReadyNodes(async));
+        }
+    }
+
+    @Test
+    void producerHasReadyNodesShouldResolveLayoutWithoutException() {
+        Properties props = new Properties();
+        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:1");
+        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, 
StringSerializer.class.getName());
+        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, 
StringSerializer.class.getName());
+
+        try (KafkaProducer<String, String> producer = new 
KafkaProducer<>(props)) {
+            assertDoesNotThrow(() -> 
KafkaNetworkHealthHelper.producerHasReadyNodes(producer));
+        }
+    }
+
+    private static Properties consumerProps(String groupProtocol) {
+        Properties props = new Properties();
+        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:1");
+        props.put(ConsumerConfig.GROUP_ID_CONFIG, "health-check-test");
+        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, 
StringDeserializer.class.getName());
+        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, 
StringDeserializer.class.getName());
+        props.put(ConsumerConfig.GROUP_PROTOCOL_CONFIG, groupProtocol);
+        return props;
+    }
+}
diff --git 
a/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/integration/health/KafkaConsumerGroupProtocolHealthCheckIT.java
 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/integration/health/KafkaConsumerGroupProtocolHealthCheckIT.java
new file mode 100644
index 000000000000..1ef43a285920
--- /dev/null
+++ 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/integration/health/KafkaConsumerGroupProtocolHealthCheckIT.java
@@ -0,0 +1,138 @@
+/*
+ * 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.component.kafka.integration.health;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.kafka.integration.common.KafkaAdminUtil;
+import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckHelper;
+import org.apache.kafka.clients.admin.AdminClient;
+import org.apache.kafka.clients.admin.FeatureMetadata;
+import org.apache.kafka.clients.admin.FinalizedVersionRange;
+import org.apache.kafka.common.Uuid;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Tags;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import static org.awaitility.Awaitility.await;
+
+/**
+ * Readiness health check coverage for {@code group.protocol=consumer} 
(KIP-848 / AsyncKafkaConsumer).
+ */
+@Timeout(60)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@DisabledIfSystemProperty(named = "kafka.instance.type", matches = 
"local-strimzi-container",
+                          disabledReason = "Test infra Kafka runs the Strimzi 
containers in a way that conflicts with multiple concurrent images")
+@Tags({ @Tag("health") })
+@EnabledOnOs(value = { OS.LINUX, OS.MAC, OS.FREEBSD, OS.OPENBSD, OS.WINDOWS },
+             architectures = { "amd64", "aarch64", "s390x" },
+             disabledReason = "This test does not run reliably on ppc64le")
+public class KafkaConsumerGroupProtocolHealthCheckIT extends 
KafkaHealthCheckTestSupport {
+
+    public static final String TOPIC = "test-health-group-protocol-" + 
Uuid.randomUuid();
+
+    @BeforeAll
+    static void checkConsumerProtocolSupport() {
+        try (AdminClient adminClient = 
KafkaAdminUtil.createAdminClient(service)) {
+            FeatureMetadata metadata = 
adminClient.describeFeatures().featureMetadata().get(10, TimeUnit.SECONDS);
+            Map<String, FinalizedVersionRange> finalizedFeatures = 
metadata.finalizedFeatures();
+            FinalizedVersionRange groupVersion = 
finalizedFeatures.get("group.version");
+            Assumptions.assumeTrue(
+                    groupVersion != null && groupVersion.maxVersionLevel() >= 
1,
+                    "Broker does not support the consumer group protocol 
(KIP-848), requires Kafka 4.0+ with group.version >= 1");
+        } catch (Exception e) {
+            Assumptions.assumeTrue(false,
+                    "Could not determine broker feature support: " + 
e.getMessage());
+        }
+    }
+
+    @Override
+    public void configureContext(CamelContext context) {
+        // NO-OP
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                String from = "kafka:" + TOPIC + "?brokers=" + 
service.getBootstrapServers()
+                              + 
"&groupId=KafkaConsumerGroupProtocolHealthCheckIT&autoOffsetReset=earliest"
+                              + 
"&keyDeserializer=org.apache.kafka.common.serialization.StringDeserializer"
+                              + 
"&valueDeserializer=org.apache.kafka.common.serialization.StringDeserializer"
+                              + 
"&autoCommitIntervalMs=1000&pollTimeoutMs=1000&autoCommitEnable=true"
+                              + "&groupProtocol=consumer";
+
+                from(from)
+                        .routeId("test-health-group-protocol-it")
+                        .to("mock:result");
+            }
+        };
+    }
+
+    @Order(1)
+    @Test
+    @DisplayName("Readiness reports UP with group.protocol=consumer when 
broker is healthy")
+    public void testReportReadyWhenReady() {
+        CamelContext context = contextExtension.getContext();
+        await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> {
+            Collection<HealthCheck.Result> results = 
HealthCheckHelper.invokeReadiness(context);
+            boolean up = results.stream().allMatch(r -> 
r.getState().equals(HealthCheck.State.UP));
+            Assertions.assertTrue(up, "readiness check with async consumer 
protocol");
+        });
+    }
+
+    @Order(2)
+    @Test
+    @DisplayName("Readiness reports DOWN with group.protocol=consumer when 
broker is shut down")
+    public void testReadinessWhenDown() {
+        CamelContext context = contextExtension.getContext();
+        service.shutdown();
+        serviceShutdown = true;
+
+        await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> {
+            Collection<HealthCheck.Result> results = 
HealthCheckHelper.invokeReadiness(context);
+            Optional<HealthCheck.Result> down
+                    = results.stream().filter(r -> 
r.getState().equals(HealthCheck.State.DOWN)).findFirst();
+            Assertions.assertTrue(down.isPresent());
+            String msg = down.get().getMessage().get();
+            Assertions.assertTrue(msg.contains("KafkaConsumer is not ready"));
+            Map<String, Object> details = down.get().getDetails();
+            Assertions.assertEquals(TOPIC, details.get("topic"));
+            Assertions.assertEquals("test-health-group-protocol-it", 
details.get("route.id"));
+        });
+    }
+}

Reply via email to