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

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

commit 30589ef8d6eeb6d4d01e2c93bb4a196d8b069ac7
Author: Christoph Deppisch <[email protected]>
AuthorDate: Thu Oct 15 17:34:12 2026 +0200

    CAMEL-25378: Fix receive mode failure recording and document edge cases
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../src/main/docs/citrus-component.adoc                  | 12 +++++++++++-
 .../apache/camel/component/citrus/CitrusEndpoint.java    | 16 +++++++++++++---
 .../component/citrus/CitrusReceiveErrorHandlingTest.java |  3 +++
 3 files changed, 27 insertions(+), 4 deletions(-)

diff --git 
a/components/camel-test/camel-test-citrus/src/main/docs/citrus-component.adoc 
b/components/camel-test/camel-test-citrus/src/main/docs/citrus-component.adoc
index 3b223533a49f..f004bb3dcff8 100644
--- 
a/components/camel-test/camel-test-citrus/src/main/docs/citrus-component.adoc
+++ 
b/components/camel-test/camel-test-citrus/src/main/docs/citrus-component.adoc
@@ -39,8 +39,10 @@ citrus:operation/name[?options]
 Where `operation` is `validate` or `receive`, and `name` is an optional 
identifier to distinguish
 multiple Citrus endpoints in the same route.
 
-* `validate` — validates exchanges without modifying them (pass-through 
validation).
+* `validate` — validates exchanges without modifying them (pass-through 
validation). Validation
+  failures are collected silently and only surface when `assertIsSatisfied()` 
is called.
 * `receive` — validates exchanges and returns a simulated response 
(request/response simulation).
+  Validation failures remain on the exchange and trigger Camel's `onException` 
handlers.
 
 // component options: START
 include::partial$component-configure-options.adoc[]
@@ -53,6 +55,10 @@ include::partial$component-endpoint-headers.adoc[]
 The Citrus endpoint acts as a producer-only endpoint. Routes send exchanges 
*to* `citrus:validate`,
 and the component validates each exchange using Citrus message validation.
 
+NOTE: Spec setup (`validate()`, `validateAll()`, `when().receive()`) must 
complete before exchanges
+start arriving. For async consumers (SEDA, timer-based routes), ensure all 
validation chains are
+fully configured before starting the `CamelContext`.
+
 === Validation modules
 
 The component does not bundle Citrus validation modules. You must add the 
required validation
@@ -394,6 +400,10 @@ The two operations handle validation failures differently 
with respect to Camel'
   `onException` handlers and redelivery policies *do* trigger, which matches 
the expected
   behavior for a request/response endpoint where callers need to see errors.
 
+NOTE: Each redelivery attempt counts as a new exchange in the mock counter. So 
if a
+`receive(1)` spec fails and Camel redelivers, the retry is exchange 2 and
+`receive(2)` applies to it — not `receive(1)` again.
+
 === Exchange properties and variables as Citrus variables
 
 All exchange properties and exchange variables are automatically propagated to 
the Citrus test
diff --git 
a/components/camel-test/camel-test-citrus/src/main/java/org/apache/camel/component/citrus/CitrusEndpoint.java
 
b/components/camel-test/camel-test-citrus/src/main/java/org/apache/camel/component/citrus/CitrusEndpoint.java
index bf58be0ad4df..efcdfc354e20 100644
--- 
a/components/camel-test/camel-test-citrus/src/main/java/org/apache/camel/component/citrus/CitrusEndpoint.java
+++ 
b/components/camel-test/camel-test-citrus/src/main/java/org/apache/camel/component/citrus/CitrusEndpoint.java
@@ -80,6 +80,10 @@ public class CitrusEndpoint extends MockEndpoint {
         return messageValidationSpec;
     }
 
+    /**
+     * Auto-incrementing variant. Do not mix with explicit {@code 
validate(index)} calls — the auto-index is unaware of
+     * explicit registrations and may overwrite them.
+     */
     public MessageValidationSpec validate() {
         return validate(nextSpecIndex.incrementAndGet());
     }
@@ -139,12 +143,15 @@ public class CitrusEndpoint extends MockEndpoint {
         // assertIsSatisfied() can report Citrus validation errors properly.
         Exception e = exchange.getException();
         if (e != null && !getFailures().contains(e)) {
-            getFailures().add(e);
             if ("validate".equals(operation)) {
-                // For validate-only mode, clear the exception so it does not 
propagate
-                // through the route — validation errors are collected, not 
thrown.
+                // For validate-only mode, collect the error and clear the 
exception so it does not
+                // propagate through the route — validation errors are 
collected, not thrown.
+                getFailures().add(e);
                 exchange.setException(null);
             }
+            // For receive mode, leave the exception on the exchange so 
Camel's onException
+            // handlers trigger. Do NOT add to failures — the error is handled 
by the route,
+            // not by assertIsSatisfied().
         }
     }
 
@@ -181,6 +188,9 @@ public class CitrusEndpoint extends MockEndpoint {
 
     public class WhenSpec {
 
+        /**
+         * Auto-incrementing variant. Do not mix with explicit {@code 
receive(index)} calls.
+         */
         public WhenMessageValidationSpec receive() {
             return receive(nextSpecIndex.incrementAndGet());
         }
diff --git 
a/components/camel-test/camel-test-citrus/src/test/java/org/apache/camel/component/citrus/CitrusReceiveErrorHandlingTest.java
 
b/components/camel-test/camel-test-citrus/src/test/java/org/apache/camel/component/citrus/CitrusReceiveErrorHandlingTest.java
index 11192d5aa55b..3b52c39aab95 100644
--- 
a/components/camel-test/camel-test-citrus/src/test/java/org/apache/camel/component/citrus/CitrusReceiveErrorHandlingTest.java
+++ 
b/components/camel-test/camel-test-citrus/src/test/java/org/apache/camel/component/citrus/CitrusReceiveErrorHandlingTest.java
@@ -63,6 +63,9 @@ class CitrusReceiveErrorHandlingTest extends CamelTestSupport 
{
 
         handled.assertIsSatisfied();
         assertThat(citrus.getReceivedCounter()).isEqualTo(1);
+        // receive mode does not add to failures — assertIsSatisfied passes 
because
+        // the error was handled by onException, not collected by the endpoint
+        citrus.assertIsSatisfied();
     }
 
     @Override

Reply via email to