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 e339a2e9019f CAMEL-23688: camel-jbang - Tests for cmd route-diagram 
and cmd span (#27969)
e339a2e9019f is described below

commit e339a2e9019fa1ce241b3bb1c2d855c5942d4794
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 17 07:51:20 2026 +0200

    CAMEL-23688: camel-jbang - Tests for cmd route-diagram and cmd span (#27969)
    
    Tests for camel cmd route-diagram of a running integration as text (25%
    covered before) and the views of camel cmd span (33%): the traces, the
    flat list of spans, the waterfall of a trace and the JSON. They found
    these bugs, fixed here:
    
    * camel cmd route-diagram --highlight never filtered the routes (the
      helper keeps them all without the order of a message), so its check
      for routes without the nodes was never reached, and node ids that no
      route has drew a diagram with nothing highlighted; now not found (3).
      Its errors went to stdout with exit 1; they go to stderr, and a
      graphical --theme without terminal graphics exits with 2.
    * camel cmd span --trace with an id that matches no trace printed a
      message and exited with 0; now not found (3). An unknown --sort of the
      traces sorted the newest first; now a usage error.
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |   8 +
 .../commands/action/CamelRouteDiagramAction.java   |  24 ++-
 .../core/commands/action/CamelSpanAction.java      |  10 +-
 .../action/CamelRouteDiagramActionRunningTest.java | 169 ++++++++++++++++++
 .../commands/action/CamelSpanActionViewsTest.java  | 195 +++++++++++++++++++++
 5 files changed, 391 insertions(+), 15 deletions(-)

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 0ecfc40763c7..81cf732576ea 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
@@ -458,6 +458,14 @@ Without an interactive terminal, such as in a script, a CI 
job (the `CI` environ
 it off (use `--follow=false` to turn it off). In a terminal, the end of the 
input (Ctrl+D) stops a command that watches
 or follows, like Enter does.
 
+=== camel-jbang - camel cmd route-diagram and camel cmd span
+
+* `camel cmd route-diagram --highlight` with node ids that no route has exits 
with `3` (it drew the diagram with
+  nothing highlighted). Its errors are printed on stderr, and a graphical 
`--theme` in a terminal without graphics exits
+  with `2`.
+* `camel cmd span --trace` with a trace id that matches no trace exits with 
`3` (it printed a message and exited with
+  `0`), and an unknown `--sort` key of the traces exits with `2` (it sorted 
the newest first).
+
 === camel-jbang - camel run: startup in the background, and input errors
 
 * `camel run --background` waits up to 60 seconds for the integration to start 
(it was 20 seconds), and
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
index 7065cfc43adc..7b4d499a3252 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
@@ -48,6 +48,7 @@ import 
org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.ActionClient;
 import org.apache.camel.dsl.jbang.core.common.ActionClient.ActionResult;
 import org.apache.camel.dsl.jbang.core.common.CliException;
+import org.apache.camel.dsl.jbang.core.common.CliExitCode;
 import org.apache.camel.dsl.jbang.core.common.PathUtils;
 import org.apache.camel.dsl.jbang.core.common.ProcessFinder;
 import org.apache.camel.dsl.jbang.core.common.ProcessFinder.CamelProcess;
@@ -183,11 +184,11 @@ public class CamelRouteDiagramAction extends 
ActionWatchCommand {
             if (!textMode) {
                 terminalGraphics = 
TerminalGraphicsManager.getBestProtocol(terminal).orElse(null);
                 if (terminalGraphics == null) {
-                    printer().println("Terminal does not support graphics 
protocols (Kitty, iTerm2, or Sixel).");
-                    printer().println(
+                    printer().printErr("Terminal does not support graphics 
protocols (Kitty, iTerm2, or Sixel).");
+                    printer().printErr(
                             "Try running in a supported terminal: Kitty, 
iTerm2, WezTerm, Ghostty, or VS Code.");
-                    printer().println("Or use --theme=ascii or --theme=unicode 
for plain text output.");
-                    return 1;
+                    printer().printErr("Or use --theme=ascii or 
--theme=unicode for plain text output.");
+                    return CliExitCode.USAGE;
                 }
             }
         }
@@ -254,20 +255,17 @@ public class CamelRouteDiagramAction extends 
ActionWatchCommand {
             throw CliException.notFound("No route matching: " + filter);
         }
 
-        // parse highlight info
+        // parse highlight info: the routes are drawn with the nodes 
highlighted
         Set<String> highlightedNodeIds = null;
         RouteDiagramHelper.HighlightStyle hlStyle = null;
-        RouteDiagramHelper.HighlightInfo highlightInfo = null;
         if (highlight != null && !highlight.isBlank()) {
             highlightedNodeIds = new 
LinkedHashSet<>(Arrays.asList(highlight.split(",")));
             hlStyle = "fail".equalsIgnoreCase(highlightStyle)
                     ? RouteDiagramHelper.HighlightStyle.FAIL
                     : RouteDiagramHelper.HighlightStyle.SUCCESS;
-            highlightInfo = new 
RouteDiagramHelper.HighlightInfo(highlightedNodeIds, List.of(), hlStyle);
-            routes = RouteDiagramHelper.filterAndOrderRoutes(routes, 
highlightInfo);
-            if (routes.isEmpty()) {
-                printer().println("No routes contain highlighted nodes");
-                return 1;
+            Set<String> ids = highlightedNodeIds;
+            if (routes.stream().flatMap(r -> r.nodes.stream()).noneMatch(n -> 
n.id != null && ids.contains(n.id))) {
+                throw CliException.notFound("No route has the nodes to 
highlight: " + highlight);
             }
         }
 
@@ -318,8 +316,8 @@ public class CamelRouteDiagramAction extends 
ActionWatchCommand {
             try {
                 image = renderer.renderDiagram(layoutRoutes, currentY, colors, 
highlightedNodeIds, hlStyle);
             } catch (IllegalStateException e) {
-                printer().println(e.getMessage());
-                return 1;
+                printer().printErr(e.getMessage());
+                return CliExitCode.FAILURE;
             }
 
             if (output != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanAction.java
index 05cc6957895e..c1b940d3ee69 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanAction.java
@@ -65,6 +65,9 @@ public class CamelSpanAction extends ActionBaseCommand {
             .numeric("duration", r -> r.durationMs)
             .text("status", r -> r.status);
 
+    // the keys of the order of the traces (see sortTraceSummary)
+    private static final List<String> TRACE_ORDER = List.of("trace", "route", 
"from", "spans", "routes", "status", "duration");
+
     public static class SortCompletionCandidates implements Iterable<String> {
 
         public SortCompletionCandidates() {
@@ -200,6 +203,10 @@ public class CamelSpanAction extends ActionBaseCommand {
             }
             tableSpans(rows);
         } else {
+            if (sort != null && !TRACE_ORDER.contains(sort.startsWith("-") ? 
sort.substring(1) : sort)) {
+                throw CliException.usage("--sort must be one of " + 
String.join(", ", TRACE_ORDER)
+                                         + " (with - in front for the reverse 
order), was: " + sort);
+            }
             List<TraceSummary> summaries = buildTraceSummaries(rows);
             if (filter != null) {
                 summaries.removeIf(ts -> 
!ts.searchText.contains(filter.toLowerCase()));
@@ -341,8 +348,7 @@ public class CamelSpanAction extends ActionBaseCommand {
             }
         }
         if (matchedTraceId == null) {
-            printer().println("No trace found matching '" + traceIdMatch + 
"'");
-            return;
+            throw CliException.notFound("No trace found matching: " + 
traceIdMatch);
         }
 
         final String tid = matchedTraceId;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramActionRunningTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramActionRunningTest.java
new file mode 100644
index 000000000000..3df2a593adc8
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramActionRunningTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+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.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+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 route-diagram of a running integration, as text (the diagrams as 
images need a terminal with graphics).
+ */
+@ExtendWith(MockitoExtension.class)
+class CamelRouteDiagramActionRunningTest extends ActionCommandTestSupport {
+
+    @TempDir
+    Path dir;
+
+    @Test
+    void asciiDiagramOfTheRoutes() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelRouteDiagramAction command = command("--theme=ascii");
+
+        assertThat(callWithResponse(command::doWatchCall, structure("route1", 
"route2"))).isZero();
+
+        
assertThat(readActionFile(TEST_PID).getString("action")).isEqualTo("route-structure");
+        assertThat(printer.getOutput()).contains("route1", "route2", 
"timer:tick", "log:out").doesNotContain("─");
+    }
+
+    @Test
+    void unicodeDiagramByDefault() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command()::doWatchCall, 
structure("route1"))).isZero();
+
+        assertThat(printer.getOutput()).contains("route1", "─");
+    }
+
+    @Test
+    void filterDrawsTheMatchingRoutes() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--theme=ascii", 
"--filter=route2")::doWatchCall,
+                structure("route1", "route2"))).isZero();
+
+        
assertThat(printer.getOutput()).contains("route2").doesNotContain("route1");
+    }
+
+    @Test
+    void filterThatMatchesNoRouteIsNotFound() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelRouteDiagramAction command = command("--theme=ascii", 
"--filter=orders");
+
+        assertThatThrownBy(() -> callWithResponse(command::doWatchCall, 
structure("route1")))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.NOT_FOUND))
+                .hasMessage("No route matching: orders");
+    }
+
+    @Test
+    void highlightOfNodesThatNoRouteHasIsNotFound() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelRouteDiagramAction command = command("--theme=ascii", 
"--highlight=nope");
+
+        // the diagram was drawn without anything highlighted
+        assertThatThrownBy(() -> callWithResponse(command::doWatchCall, 
structure("route1")))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.NOT_FOUND))
+                .hasMessage("No route has the nodes to highlight: nope");
+        assertThat(printer.getStdout()).isEmpty();
+    }
+
+    @Test
+    void highlightDrawsTheRoutesWithTheNodesHighlighted() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--theme=ascii", 
"--highlight=to2")::doWatchCall,
+                structure("route1", "route2"))).isZero();
+
+        assertThat(printer.getOutput()).contains("route1", "route2");
+    }
+
+    @Test
+    void textDiagramIntoAFile() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        Path png = dir.resolve("out").resolve("diagram.png");
+
+        assertThat(callWithResponse(command("--theme=ascii", "--output=" + 
png)::doWatchCall, structure("route1")))
+                .isZero();
+
+        // a text theme writes text, next to where the image would go
+        Path txt = dir.resolve("out").resolve("diagram.txt");
+        assertThat(Files.readString(txt)).contains("route1", "timer:tick");
+        assertThat(printer.getOutput()).contains("Diagram saved to: " + 
txt.toAbsolutePath());
+    }
+
+    @Test
+    void anIntegrationWithoutRoutesIsAFailure() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelRouteDiagramAction command = command("--theme=ascii");
+
+        assertThatThrownBy(() -> callWithResponse(command::doWatchCall, 
structure()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.FAILURE))
+                .hasMessage("No routes found");
+    }
+
+    private CamelRouteDiagramAction command(String... args) {
+        CamelRouteDiagramAction command = new CamelRouteDiagramAction(new 
CamelJBangMain().withPrinter(printer));
+        CommandLine.populateCommand(command, args);
+        command.files = List.of("myApp");
+        return command;
+    }
+
+    // the answer of the route-structure action: each route from a timer to a 
log
+    private static JsonObject structure(String... routeIds) {
+        JsonArray routes = new JsonArray();
+        int n = 0;
+        for (String routeId : routeIds) {
+            n++;
+            JsonArray code = new JsonArray();
+            code.add(line("from", "timer:tick", 0, "from" + n));
+            code.add(line("to", "log:out", 1, "to" + n));
+            JsonObject route = new JsonObject();
+            route.put("routeId", routeId);
+            route.put("code", code);
+            routes.add(route);
+        }
+        JsonObject answer = new JsonObject();
+        answer.put("routes", routes);
+        return answer;
+    }
+
+    private static JsonObject line(String type, String code, int level, String 
id) {
+        JsonObject line = new JsonObject();
+        line.put("type", type);
+        line.put("code", code);
+        line.put("level", level);
+        line.put("id", id);
+        return line;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanActionViewsTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanActionViewsTest.java
new file mode 100644
index 000000000000..55c64190513d
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanActionViewsTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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 span: the traces of the spans an integration captured, a flat 
list of the spans, and the waterfall of a
+ * trace.
+ */
+@ExtendWith(MockitoExtension.class)
+class CamelSpanActionViewsTest extends ActionCommandTestSupport {
+
+    @Test
+    void tracesWithTheirSpans() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command(), spans())).isZero();
+
+        String out = printer.getOutput();
+        assertThat(out).contains("TRACE-ID", "orders", "payments", "ERROR", 
"OK", "kafka");
+    }
+
+    @Test
+    void filterTheTraces() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--filter=payments"), 
spans())).isZero();
+
+        
assertThat(printer.getOutput()).contains("payments").doesNotContain("orders");
+    }
+
+    @Test
+    void sortTheTracesBySpans() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--sort=spans"), 
spans())).isZero();
+
+        // the trace with the most spans first
+        String out = printer.getOutput();
+        assertThat(out.indexOf("orders")).isLessThan(out.indexOf("payments"));
+    }
+
+    @Test
+    void unknownSortOfTheTracesIsAUsageError() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelSpanAction command = command("--sort=size");
+
+        assertThatThrownBy(() -> callWithResponse(command, spans()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.USAGE))
+                .hasMessageStartingWith("--sort must be one of trace, route, 
from, spans, routes, status, duration");
+    }
+
+    @Test
+    void flatListOfTheSpansByDuration() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--flat", "--sort=-duration"), 
spans())).isZero();
+
+        String out = printer.getOutput();
+        // the slowest span (the kafka call of 40ms) first
+        assertThat(out).contains("kafka:billing", "timer://orders");
+        
assertThat(out.indexOf("kafka:billing")).isLessThan(out.indexOf("log:done"));
+    }
+
+    @Test
+    void waterfallOfATrace() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--trace=aaaa"), 
spans())).isZero();
+
+        assertThat(printer.getOutput()).contains("timer://orders", 
"kafka:billing", "log:done")
+                .doesNotContain("timer://payments");
+    }
+
+    @Test
+    void waterfallOfATraceThatDoesNotExistIsNotFound() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelSpanAction command = command("--trace=ffff");
+
+        assertThatThrownBy(() -> callWithResponse(command, spans()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.NOT_FOUND))
+                .hasMessage("No trace found matching: ffff");
+    }
+
+    @Test
+    void jsonOfTheSpansOfATrace() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--json", "--trace=bbbb"), 
spans())).isZero();
+
+        JsonArray spans = Jsoner.deserialize(printer.getOutput(), (JsonArray) 
null);
+        assertThat(spans).hasSize(1);
+        JsonObject span = spans.getMap(0);
+        assertThat(span.getString("routeId")).isEqualTo("payments");
+    }
+
+    @Test
+    void noSpansYet() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        JsonObject answer = new JsonObject(Map.of("enabled", true, "spans", 
new JsonArray()));
+
+        assertThat(callWithResponse(command(), answer)).isZero();
+
+        assertThat(printer.getStderr()).contains("No spans captured yet.");
+    }
+
+    @Test
+    void anIntegrationWithoutTheExporterIsAFailure() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelSpanAction command = command();
+
+        assertThatThrownBy(() -> callWithResponse(command, new 
JsonObject(Map.of("enabled", false))))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.FAILURE))
+                .hasMessageContaining("--observe");
+    }
+
+    private CamelSpanAction command(String... args) {
+        CamelSpanAction command = new CamelSpanAction(new 
CamelJBangMain().withPrinter(printer));
+        CommandLine.populateCommand(command,
+                Stream.concat(Stream.of("myApp", "--logging-color=false"), 
Arrays.stream(args)).toArray(String[]::new));
+        return command;
+    }
+
+    // two traces: orders (three spans, a kafka call that failed) and payments 
(one span)
+    private static JsonObject spans() {
+        JsonArray spans = new JsonArray(
+                List.of(
+                        span("aaaa1111", "s1", "", "timer://orders", "orders", 
"OK", 0, 50, null),
+                        span("aaaa1111", "s2", "s1", "kafka:billing", 
"orders", "ERROR", 5, 45, "kafka"),
+                        span("aaaa1111", "s3", "s1", "log:done", "orders", 
"OK", 46, 48, null),
+                        span("bbbb2222", "s4", "", "timer://payments", 
"payments", "OK", 100, 101, null)));
+        JsonObject answer = new JsonObject();
+        answer.put("enabled", true);
+        answer.put("spans", spans);
+        return answer;
+    }
+
+    private static JsonObject span(
+            String traceId, String spanId, String parentSpanId, String name, 
String routeId, String status, long startMs,
+            long endMs, String scheme) {
+        JsonObject attributes = new JsonObject();
+        attributes.put("exchangeId", "E-" + traceId);
+        if (scheme != null) {
+            attributes.put("url.scheme", scheme);
+        }
+        JsonObject span = new JsonObject();
+        span.put("traceId", traceId);
+        span.put("spanId", spanId);
+        span.put("parentSpanId", parentSpanId);
+        span.put("name", name);
+        span.put("kind", "INTERNAL");
+        span.put("status", status);
+        span.put("durationMs", endMs - startMs);
+        span.put("routeId", routeId);
+        span.put("startEpochNanos", startMs * 1_000_000L);
+        span.put("endEpochNanos", endMs * 1_000_000L);
+        span.put("attributes", attributes);
+        return span;
+    }
+}

Reply via email to