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

davsclaus 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 c93c26fdecb8 CAMEL-23688: camel-jbang - Tests for cmd browse and cmd 
stub (#27973)
c93c26fdecb8 is described below

commit c93c26fdecb80c1705487216a2e4dba787f886c4
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Oct 16 22:17:08 2026 +0200

    CAMEL-23688: camel-jbang - Tests for cmd browse and cmd stub (#27973)
    
    Tests for the views of camel cmd browse (57% covered) and camel cmd stub
    (46%): the endpoints or queues, their order, --mask, --filter, the dump
    of the messages (--only-body, --grep, latest first) and the JSON. They
    found that camel cmd browse --endpoint with an endpoint that the
    integration cannot browse printed an empty table and exited with 0; now
    not found (3).
    
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |   5 +
 .../core/commands/action/CamelBrowseAction.java    |   6 +
 .../action/CamelBrowseActionViewsTest.java         | 171 +++++++++++++++++++++
 .../commands/action/CamelStubActionViewsTest.java  | 154 +++++++++++++++++++
 4 files changed, 336 insertions(+)

diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 6f020a7441ff..3668b9465e35 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -422,6 +422,11 @@ The JSON of the listings (`--json`) changes, so that a 
script can use the values
   integration that stops right away prints the end of its log and exits with 
`1`; one that is still starting after
   `--timeout` exits with `124` and is left running.
 
+=== camel-jbang - camel cmd browse of an endpoint that cannot be browsed
+
+`camel cmd browse --endpoint` with an endpoint that the integration cannot 
browse, or that it does not have, exits with
+`3` (it printed an empty table and exited with `0`).
+
 === camel-jbang - no command waits for a terminal that is not there
 
 Without an interactive terminal, such as in a script, a CI job (the `CI` 
environment variable is set) or an AI agent:
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBrowseAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBrowseAction.java
index c76dc1192daa..e70df7b37a9d 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBrowseAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBrowseAction.java
@@ -27,6 +27,7 @@ import com.github.freva.asciitable.Column;
 import com.github.freva.asciitable.HorizontalAlign;
 import com.github.freva.asciitable.OverflowBehaviour;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CliException;
 import org.apache.camel.dsl.jbang.core.common.CopyableRow;
 import org.apache.camel.dsl.jbang.core.common.JsonOutput;
 import org.apache.camel.dsl.jbang.core.common.ListingFormat;
@@ -182,6 +183,11 @@ public class CamelBrowseAction extends ActionBaseCommand {
             row = row.copy();
         }
 
+        if (endpoint != null && rows.isEmpty()) {
+            // the endpoint asked for is not one the integration can browse
+            throw CliException.notFound("No browsable endpoint matches: " + 
endpoint);
+        }
+
         // sort rows
         rows.sort(ORDER.comparator(sort));
 
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBrowseActionViewsTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBrowseActionViewsTest.java
new file mode 100644
index 000000000000..4f12ee77319f
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBrowseActionViewsTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.dsl.jbang.core.commands.action;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CliException;
+import org.apache.camel.dsl.jbang.core.common.CliExitCode;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * camel cmd browse: the endpoints of an integration that queue messages, and 
the messages queued in them.
+ */
+@ExtendWith(MockitoExtension.class)
+class CamelBrowseActionViewsTest extends ActionCommandTestSupport {
+
+    @Test
+    void theEndpointsAndTheirSize() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command(), endpoints())).isZero();
+
+        JsonObject action = readActionFile(TEST_PID);
+        assertThat(action.getString("filter")).isEqualTo("*");
+        assertThat(action.getInteger("limit")).isEqualTo(100);
+        String out = printer.getOutput();
+        // the queue holds at least the limit
+        assertThat(out).contains("seda://orders", "seda://payments", "100+");
+        
assertThat(out.indexOf("seda://orders")).isLessThan(out.indexOf("seda://payments"));
+    }
+
+    @Test
+    void sortBySize() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--sort=-size"), 
endpoints())).isZero();
+
+        String out = printer.getOutput();
+        
assertThat(out.indexOf("seda://payments")).isLessThan(out.indexOf("seda://orders"));
+    }
+
+    @Test
+    void maskHidesTheSecretsOfTheEndpoints() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--mask", "--wide-uri"), 
endpoints())).isZero();
+
+        
assertThat(printer.getOutput()).contains("seda://payments").doesNotContain("secret");
+    }
+
+    @Test
+    void dumpTheMessages() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--endpoint=seda:orders", 
"--dump", "--tail=5", "--body-max-chars=200"),
+                endpoints())).isZero();
+
+        JsonObject action = readActionFile(TEST_PID);
+        assertThat(action.getString("filter")).isEqualTo("seda:orders");
+        assertThat(action.getBoolean("dump")).isTrue();
+        assertThat(action.getInteger("tail")).isEqualTo(5);
+        assertThat(action.getInteger("bodyMaxChars")).isEqualTo(200);
+        assertThat(printer.getOutput()).contains("Browse Message: (1/2)", 
"Browse Message: (2/2)", "order-1", "orderId");
+    }
+
+    @Test
+    void dumpOnlyTheBodies() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--dump", "--only-body"), 
endpoints())).isZero();
+
+        
assertThat(printer.getOutput()).contains("order-1").doesNotContain("orderId");
+    }
+
+    @Test
+    void theEndpointsAsJson() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--json", "--dump"), 
endpoints())).isZero();
+
+        JsonArray rows = Jsoner.deserialize(printer.getOutput(), (JsonArray) 
null);
+        assertThat(rows).hasSize(2);
+        JsonObject orders = rows.getMap(0);
+        assertThat(orders.getString("endpointUri")).isEqualTo("seda://orders");
+        assertThat(orders.getInteger("queueSize")).isEqualTo(2);
+        assertThat((List<?>) orders.get("messages")).hasSize(2);
+    }
+
+    @Test
+    void anEndpointThatCannotBeBrowsedIsNotFound() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelBrowseAction command = command("--endpoint=kafka:orders");
+
+        assertThatThrownBy(() -> callWithResponse(command, new 
JsonObject(Map.of("browse", new JsonArray()))))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.NOT_FOUND))
+                .hasMessage("No browsable endpoint matches: kafka:orders");
+    }
+
+    @Test
+    void unknownSortIsAUsageError() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelBrowseAction command = command("--sort=age");
+
+        assertThatThrownBy(() -> callWithResponse(command, endpoints()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.USAGE));
+    }
+
+    private CamelBrowseAction command(String... args) {
+        CamelBrowseAction command = new CamelBrowseAction(new 
CamelJBangMain().withPrinter(printer));
+        CommandLine.populateCommand(command,
+                Stream.concat(Stream.of("myApp", "--logging-color=false"), 
Arrays.stream(args)).toArray(String[]::new));
+        return command;
+    }
+
+    // the answer of the browse action: orders with two messages, payments at 
its limit
+    private static JsonObject endpoints() {
+        JsonObject orders = new JsonObject();
+        orders.put("endpointUri", "seda://orders");
+        orders.put("queueSize", 2);
+        orders.put("limit", 100);
+        orders.put("position", 0);
+        orders.put("firstTimestamp", System.currentTimeMillis() - 60000);
+        orders.put("lastTimestamp", System.currentTimeMillis() - 1000);
+        orders.put("messages", new JsonArray(List.of(message("A-1", 
"order-1"), message("A-2", "order-2"))));
+        JsonObject payments = new JsonObject();
+        payments.put("endpointUri", "seda://payments?password=secret");
+        payments.put("queueSize", 100);
+        payments.put("limit", 100);
+        payments.put("position", 0);
+        return new JsonObject(Map.of("browse", new JsonArray(List.of(orders, 
payments))));
+    }
+
+    private static JsonObject message(String exchangeId, String body) {
+        JsonObject header = new JsonObject(Map.of("key", "orderId", "type", 
"java.lang.String", "value", "42"));
+        JsonObject value = new JsonObject(Map.of("type", "java.lang.String", 
"value", body));
+        JsonObject message = new JsonObject();
+        message.put("messageType", "DefaultMessage");
+        message.put("headers", new JsonArray(List.of(header)));
+        message.put("body", value);
+        return new JsonObject(Map.of("exchangeId", exchangeId, "message", 
message));
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelStubActionViewsTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelStubActionViewsTest.java
new file mode 100644
index 000000000000..2f9e42f57651
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelStubActionViewsTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.dsl.jbang.core.commands.action;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CliException;
+import org.apache.camel.dsl.jbang.core.common.CliExitCode;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * camel cmd stub: the stub queues of an integration (camel run --stub), and 
the messages in them.
+ */
+@ExtendWith(MockitoExtension.class)
+class CamelStubActionViewsTest extends ActionCommandTestSupport {
+
+    @Test
+    void theQueues() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command(), queues())).isZero();
+
+        JsonObject action = readActionFile(TEST_PID);
+        assertThat(action.getString("action")).isEqualTo("stub");
+        String out = printer.getOutput();
+        assertThat(out).contains("kafka:orders", "jms:payments");
+        
assertThat(out.indexOf("jms:payments")).isLessThan(out.indexOf("kafka:orders"));
+    }
+
+    @Test
+    void sortByTotal() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--sort=-total"), 
queues())).isZero();
+
+        String out = printer.getOutput();
+        
assertThat(out.indexOf("kafka:orders")).isLessThan(out.indexOf("jms:payments"));
+    }
+
+    @Test
+    void filterTheQueues() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--filter=kafka*"), 
queues())).isZero();
+
+        
assertThat(printer.getOutput()).contains("kafka:orders").doesNotContain("jms:payments");
+    }
+
+    @Test
+    void browseTheMessagesLatestFirst() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--browse", "--filter=kafka*"), 
queues())).isZero();
+
+        assertThat(readActionFile(TEST_PID).getBoolean("browse")).isTrue();
+        String out = printer.getOutput();
+        assertThat(out).contains("order-1", "order-2", "orderId");
+        assertThat(out.indexOf("order-2")).isLessThan(out.indexOf("order-1"));
+    }
+
+    @Test
+    void browseTheMatchingMessages() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--browse", "--filter=kafka*", 
"--grep=order-2", "--show-headers=false"),
+                queues())).isZero();
+
+        String out = printer.getOutput().replaceAll("\u001B\\[[;\\d]*m", "");
+        assertThat(out).contains("order-2").doesNotContain("order-1", 
"orderId");
+    }
+
+    @Test
+    void theQueuesAsJson() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--json"), queues())).isZero();
+
+        JsonArray rows = Jsoner.deserialize(printer.getOutput(), (JsonArray) 
null);
+        assertThat(rows).hasSize(2);
+        JsonObject first = rows.getMap(0);
+        assertThat(first.getString("queue")).isEqualTo("jms:payments");
+        assertThat(first.getInteger("size")).isZero();
+    }
+
+    @Test
+    void unknownSortIsAUsageError() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelStubAction command = command("--sort=size");
+
+        assertThatThrownBy(() -> callWithResponse(command, queues()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.USAGE));
+    }
+
+    private CamelStubAction command(String... args) {
+        CamelStubAction command = new CamelStubAction(new 
CamelJBangMain().withPrinter(printer));
+        CommandLine.populateCommand(command,
+                Stream.concat(Stream.of("myApp", "--logging-color=false"), 
Arrays.stream(args)).toArray(String[]::new));
+        return command;
+    }
+
+    // the answer of the stub action: kafka:orders with two messages, an empty 
jms:payments
+    private static JsonObject queues() {
+        JsonObject orders = new JsonObject();
+        orders.put("name", "kafka:orders");
+        orders.put("max", 1000);
+        orders.put("size", 2);
+        orders.put("endpointUri", "stub://kafka:orders");
+        orders.put("messages", new JsonArray(List.of(message("order-1"), 
message("order-2"))));
+        JsonObject payments = new JsonObject();
+        payments.put("name", "jms:payments");
+        payments.put("max", 1000);
+        payments.put("size", 0);
+        payments.put("endpointUri", "stub://jms:payments");
+        return new JsonObject(Map.of("queues", new JsonArray(List.of(orders, 
payments))));
+    }
+
+    private static JsonObject message(String body) {
+        JsonObject header = new JsonObject(Map.of("key", "orderId", "type", 
"java.lang.String", "value", "42"));
+        JsonObject message = new JsonObject();
+        message.put("exchangeId", "E-" + body);
+        message.put("exchangePattern", "InOnly");
+        message.put("headers", new JsonArray(List.of(header)));
+        message.put("body", new JsonObject(Map.of("type", "java.lang.String", 
"value", body)));
+        return new JsonObject(Map.of("message", message));
+    }
+}

Reply via email to