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 1afa3af6f48b chore: camel-jbang - the span views and the test scaffold
read remote and internal from the catalog
1afa3af6f48b is described below
commit 1afa3af6f48b54763b2982726ccb7e29cba93948
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Oct 14 21:51:21 2026 +0200
chore: camel-jbang - the span views and the test scaffold read remote and
internal from the catalog
The remote components of a trace (camel get span and the Spans tab of the
TUI) came from a list of schemes that are not remote (direct, seda, mock,
log, bean, class), so a timer or an xslt step counted as a remote call.
They now ask the catalog's remote flag, through ProjectOverview.isRemote.
camel_route_test_scaffold kept a list of trivial schemes that a test does
not mock (log, direct, seda, mock, controlbus, stub). It now keeps the
components the catalog marks internal (CAMEL-25652), read from the
server's own catalog, as an older one loaded for the call has no flag.
controlbus is mocked now, as it is not internal.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../modules/ROOT/pages/camel-jbang-mcp.adoc | 8 +++++---
.../core/commands/action/CamelSpanAction.java | 20 +++++++++++++++-----
.../core/commands/action/CamelSpanActionTest.java | 14 ++++++++++++++
.../dsl/jbang/core/commands/mcp/TestInfraData.java | 10 ----------
.../jbang/core/commands/mcp/TestScaffoldTools.java | 17 +++++++++++++++--
.../core/commands/mcp/TestScaffoldToolsTest.java | 22 ++++++++++++++++++++++
.../dsl/jbang/core/commands/tui/SpansTab.java | 12 ++++++++----
.../core/commands/tui/SpansTabRenderTest.java | 11 +++++++++++
8 files changed, 90 insertions(+), 24 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
index 8e2b9bd1962d..87f24622772d 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
@@ -560,9 +560,11 @@ client to the next.
| Generates a JUnit 5 test skeleton from a Camel route definition (YAML or
XML). Accepts an optional `format`
(`yaml` or `xml`, default `yaml`) and `runtime` (`main` or `spring-boot`,
default `main`). For `main` runtime,
the generated test extends `CamelTestSupport`; for `spring-boot`, it uses
`@CamelSpringBootTest` with
- `@SpringBootTest`. The tool replaces non-trivial producer endpoints with
mock endpoints, generates
- `@RegisterExtension` stubs for infrastructure components (Kafka,
JMS/Artemis, MongoDB, PostgreSQL, Cassandra,
- Elasticsearch, Redis, RabbitMQ, FTP, Consul, NATS, Pulsar, CouchDB,
Infinispan, MinIO, Solr), and produces
+ `@SpringBootTest`. The tool replaces producer endpoints with mock endpoints,
except those of the components
+ the Camel catalog marks internal (`direct:`, `seda:`, `log:`, `mock:` and
the like), which stay inside the
+ CamelContext. It generates `@RegisterExtension` stubs for infrastructure
components (Kafka, JMS/Artemis,
+ MongoDB, PostgreSQL, Cassandra, Elasticsearch, Redis, RabbitMQ, FTP, Consul,
NATS, Pulsar, CouchDB, Infinispan,
+ MinIO, Solr), and produces
a `NotifyBuilder` pattern for timer-based routes or `template.sendBody()`
for direct/seda consumers. Returns
the generated test code, detected components, mock endpoint mappings,
test-infra services, and required Maven
test dependencies.
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 5733ba46a53c..6bc35e6a1c54 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
@@ -27,12 +27,16 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import com.github.freva.asciitable.AsciiTable;
import com.github.freva.asciitable.Column;
import com.github.freva.asciitable.HorizontalAlign;
import com.github.freva.asciitable.OverflowBehaviour;
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.commands.ai.ProjectOverview;
import org.apache.camel.dsl.jbang.core.common.PathUtils;
import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
import org.apache.camel.dsl.jbang.core.common.TerminalWidthHelper;
@@ -663,11 +667,17 @@ public class CamelSpanAction extends ActionBaseCommand {
return row.attributes != null &&
"EVENT_RECEIVED".equals(row.attributes.get("op"));
}
- private static boolean isRemoteScheme(String scheme) {
- return scheme != null
- && !"direct".equals(scheme) && !"seda".equals(scheme)
- && !"mock".equals(scheme) && !"log".equals(scheme)
- && !"bean".equals(scheme) && !"class".equals(scheme);
+ // whether the component of a span talks to a remote system, as the
catalog says (a scheme it does not know counts as
+ // remote); remembered per scheme, as watch builds the trace summaries
again and again
+ private static final Map<String, Boolean> REMOTE_SCHEMES = new
ConcurrentHashMap<>();
+
+ static boolean isRemoteScheme(String scheme) {
+ return scheme != null && REMOTE_SCHEMES.computeIfAbsent(scheme, s ->
ProjectOverview.isRemote(s, OwnCatalog.CATALOG));
+ }
+
+ /** The catalog of the CLI's own Camel version, created on first use. */
+ private static final class OwnCatalog {
+ static final CamelCatalog CATALOG = new DefaultCamelCatalog();
}
private static String spanLabel(Row row) {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanActionTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanActionTest.java
index e3b3b4930b4b..b355dbe8da99 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanActionTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSpanActionTest.java
@@ -24,6 +24,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -86,4 +87,17 @@ class CamelSpanActionTest extends ActionCommandTestSupport {
response.put("spans", spans);
return response;
}
+
+ @Test
+ void remoteComponentsComeFromTheCatalog() {
+ // as the catalog marks them; a scheme it does not know counts as
remote
+ assertTrue(CamelSpanAction.isRemoteScheme("kafka"));
+ assertTrue(CamelSpanAction.isRemoteScheme("acme"));
+ assertFalse(CamelSpanAction.isRemoteScheme("direct"));
+ assertFalse(CamelSpanAction.isRemoteScheme("bean"));
+ // not in the list the check used to keep: a timer or an xslt step is
not a remote call
+ assertFalse(CamelSpanAction.isRemoteScheme("timer"));
+ assertFalse(CamelSpanAction.isRemoteScheme("xslt"));
+ assertFalse(CamelSpanAction.isRemoteScheme(null));
+ }
}
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestInfraData.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestInfraData.java
index a5d542b26c85..45976aa34105 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestInfraData.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestInfraData.java
@@ -36,9 +36,6 @@ import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class TestInfraData {
- /** Component schemes that are trivial and should not be replaced with
mocks. */
- private static final Set<String> TRIVIAL_SCHEMES = Set.of("log", "direct",
"seda", "mock", "controlbus", "stub");
-
/** Component schemes that are internally triggered (user can send
messages to them). */
private static final Set<String> SENDABLE_SCHEMES = Set.of("direct",
"seda");
@@ -169,13 +166,6 @@ public class TestInfraData {
return List.copyOf(TEST_INFRA_MAP.keySet());
}
- /**
- * Get the set of trivial component schemes that should not be replaced
with mocks.
- */
- public Set<String> getTrivialSchemes() {
- return TRIVIAL_SCHEMES;
- }
-
/**
* Get the set of component schemes that are internally triggered (user
can send messages to them).
*/
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
index af5620e544fc..b140bf47e319 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldTools.java
@@ -110,11 +110,14 @@ public class TestScaffoldTools {
// Collect all unique component schemes
List<String> allSchemes = collectSchemes(fromEndpoints,
toEndpoints);
- // Determine which to endpoints need mock replacements
+ // Determine which to endpoints need mock replacements: all but
those of the components the catalog marks
+ // internal (direct, seda, log, mock and the like), which stay
inside the CamelContext. The server's own
+ // catalog has the flag; an older one loaded for this call would
not.
+ CamelCatalog ownCatalog = catalogService.getDefaultCatalog();
List<String> mockEndpoints = toEndpoints.stream()
.filter(uri -> {
String scheme = extractScheme(uri);
- return scheme != null &&
!testInfraData.getTrivialSchemes().contains(scheme);
+ return scheme != null && !isInternal(ownCatalog,
scheme);
})
.toList();
@@ -183,6 +186,16 @@ public class TestScaffoldTools {
// ---- Scheme extraction and mock naming ----
+ /** Whether the catalog marks the component internal: it stays inside the
CamelContext, so a test keeps it real. */
+ static boolean isInternal(CamelCatalog catalog, String scheme) {
+ try {
+ ComponentModel model = catalog.componentModel(scheme);
+ return model != null && model.isInternal();
+ } catch (RuntimeException e) {
+ return false;
+ }
+ }
+
String extractScheme(String uri) {
if (uri == null || uri.isEmpty()) {
return null;
diff --git
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
index da5d5760779e..1c96273bbdef 100644
---
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
+++
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/TestScaffoldToolsTest.java
@@ -265,6 +265,28 @@ class TestScaffoldToolsTest {
assertThat(mocks).isEmpty();
}
+ @Test
+ void mocksWhatLeavesTheCamelContextButNotInternalComponents() throws
Exception {
+ String route = """
+ - route:
+ from:
+ uri: direct:start
+ steps:
+ - to: caffeine-cache:orders
+ - to: disruptor:work
+ - to: controlbus:route?routeId=other&action=stop
+ - to: kafka:orders
+ """;
+
+ String json = tools.camel_route_test_scaffold(route, "yaml", "main",
null, null);
+ JsonObject result = (JsonObject) Jsoner.deserialize(json);
+
+ // the components the catalog marks internal stay real; controlbus is
not internal, it can evaluate an expression
+ JsonArray mocks = result.getCollection("mockEndpoints");
+ assertThat(mocks).extracting(m -> ((JsonObject)
m).getString("originalUri"))
+ .containsExactly("controlbus:route?routeId=other&action=stop",
"kafka:orders");
+ }
+
// ---- Spring Boot runtime test generation ----
@Test
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTab.java
index 6c96fa755631..d308ff38e5a9 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTab.java
@@ -25,6 +25,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import dev.tamboui.layout.Constraint;
@@ -49,6 +50,7 @@ import dev.tamboui.widgets.table.Cell;
import dev.tamboui.widgets.table.Row;
import dev.tamboui.widgets.table.Table;
import dev.tamboui.widgets.table.TableState;
+import org.apache.camel.dsl.jbang.core.commands.ai.ProjectOverview;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
@@ -905,11 +907,13 @@ class SpansTab extends AbstractTab {
return span.name();
}
- private static boolean isRemoteScheme(String scheme) {
+ // whether the component of a span talks to a remote system, as the
catalog says (a scheme it does not know counts as
+ // remote); remembered per scheme, as the trace summaries are built on
every refresh
+ private static final Map<String, Boolean> REMOTE_SCHEMES = new
ConcurrentHashMap<>();
+
+ static boolean isRemoteScheme(String scheme) {
return scheme != null
- && !"direct".equals(scheme) && !"seda".equals(scheme)
- && !"mock".equals(scheme) && !"log".equals(scheme)
- && !"bean".equals(scheme) && !"class".equals(scheme);
+ && REMOTE_SCHEMES.computeIfAbsent(scheme, s ->
ProjectOverview.isRemote(s, ProjectOverviewAssist.catalog()));
}
private static int compareNullable(String a, String b) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTabRenderTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTabRenderTest.java
index 88d5e1dd603b..f0afae119cda 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTabRenderTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SpansTabRenderTest.java
@@ -29,6 +29,7 @@ import dev.tamboui.text.Span;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -160,4 +161,14 @@ class SpansTabRenderTest {
"route1", null, "camel", Map.of());
}
+ @Test
+ void remoteComponentsComeFromTheCatalog() {
+ // as the catalog marks them; a scheme it does not know counts as
remote
+ assertThat(SpansTab.isRemoteScheme("kafka")).isTrue();
+ assertThat(SpansTab.isRemoteScheme("acme")).isTrue();
+ assertThat(SpansTab.isRemoteScheme("log")).isFalse();
+ // not in the list the check used to keep: a timer or an xslt step is
not a remote call
+ assertThat(SpansTab.isRemoteScheme("timer")).isFalse();
+ assertThat(SpansTab.isRemoteScheme("xslt")).isFalse();
+ }
}