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 f246c1e735cc CAMEL-23817: camel-jbang - Fix test init command
f246c1e735cc is described below

commit f246c1e735cc40b831d5cccfc83c46679e412ec3
Author: Christoph Deppisch <[email protected]>
AuthorDate: Fri Jun 26 07:47:36 2026 +0200

    CAMEL-23817: camel-jbang - Fix test init command
    
    Move custom init logic from CitrusExecutionStrategy into a dedicated
    TestInit command class. Fix isCitrusCommand to use instanceof instead
    of fragile string-based class name check. Add unit tests for the test
    plugin covering init, run, inspect and list commands.
    
    Closes #24256
---
 .gitignore                                         |   3 +-
 .../dsl/jbang/core/commands/test/TestInit.java     | 126 +++++++++++++++++++
 .../dsl/jbang/core/commands/test/TestPlugin.java   |  74 +----------
 .../jbang/core/commands/test/TestPluginTest.java   | 138 +++++++++++++++++++++
 .../src/test/resources/my-sample.citrus.it.yaml    |  27 ++++
 5 files changed, 298 insertions(+), 70 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6a653dcd0b9f..7c7295bcf5b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,7 +30,8 @@ node_modules/
 mvnd.zip*
 .camel-jbang
 .camel-jbang-run
+.citrus-jbang
 .mvn/.develocity/develocity-workspace-id
 backlog
 .claude
-.omc
\ No newline at end of file
+.omc
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestInit.java
 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestInit.java
new file mode 100644
index 000000000000..8d8eea6fd034
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestInit.java
@@ -0,0 +1,126 @@
+/*
+ * 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.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Stack;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.catalog.VersionHelper;
+import org.apache.camel.dsl.jbang.core.commands.ExportHelper;
+import org.apache.camel.util.IOHelper;
+import org.citrusframework.CitrusSettings;
+import org.citrusframework.CitrusVersion;
+import org.citrusframework.jbang.CitrusJBangMain;
+import org.citrusframework.jbang.commands.CitrusCommand;
+import org.citrusframework.util.ClassLoaderHelper;
+import org.citrusframework.util.FileUtils;
+import picocli.CommandLine;
+
+import static java.nio.file.Files.writeString;
+
+/**
+ * Automatically uses test subfolder as a working directory for creating new 
tests. Automatically adds a
+ * citrus-application.properties configuration if not present.
+ */
[email protected](name = "init", description = "Creates a new Citrus test")
+public class TestInit extends CitrusCommand {
+
+    @CommandLine.Parameters(description = "Name of test file (or a github 
link)", arity = "1",
+                            paramLabel = "<file>", parameterConsumer = 
TestInit.FileConsumer.class)
+    private Path filePath; // Defined only for file path completion; the field 
never used
+
+    private String file;
+
+    @CommandLine.Option(names = { "--directory" },
+                        description = "Directory where the files will be 
created", defaultValue = TestPlugin.TEST_DIR)
+    private String directory;
+
+    public TestInit(CitrusJBangMain citrus) {
+        super(citrus);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        String ext = FileUtils.getFileExtension(file);
+        String name = FileUtils.getBaseName(file);
+        String content;
+        try (InputStream is = 
ClassLoaderHelper.getClassLoader().getResourceAsStream("templates/" + ext + 
".tmpl")) {
+            if (is == null) {
+                printer().println("Error: Unsupported file type: " + ext);
+                return 1;
+            }
+            content = FileUtils.readToString(is, StandardCharsets.UTF_8);
+        }
+
+        Path currentDir = Paths.get(".");
+        Path workingDir;
+        if (directory.equals(currentDir.getFileName().toString())) {
+            // current directory is already the target subfolder
+            workingDir = currentDir;
+        } else if (currentDir.resolve(directory).toFile().exists()) {
+            // navigate to existing target subfolder
+            workingDir = currentDir.resolve(directory);
+            System.setProperty(CitrusSettings.RESOURCES_WORKDIR_PROPERTY, 
workingDir.toString());
+        } else if (currentDir.resolve(directory).toFile().mkdirs()) {
+            // create target subfolder and navigate to it
+            workingDir = currentDir.resolve(directory);
+            System.setProperty(CitrusSettings.RESOURCES_WORKDIR_PROPERTY, 
workingDir.toString());
+        } else {
+            throw new RuntimeCamelException("Failed to create working 
directory in: " + currentDir);
+        }
+
+        File target = workingDir.resolve(file).toFile();
+        content = content.replaceFirst("\\{\\{ \\.Name }}", name);
+
+        writeString(target.toPath(), content);
+
+        // Create Citrus application properties if not present
+        if 
(!workingDir.resolve(CitrusSettings.getApplicationPropertiesFile()).toFile().exists())
 {
+            Path citrusApplicationProperties = 
workingDir.resolve(CitrusSettings.getApplicationPropertiesFile());
+            try (InputStream is
+                    = TestPlugin.class.getClassLoader()
+                            
.getResourceAsStream("templates/citrus-application-properties.tmpl")) {
+                String context = IOHelper.loadText(is);
+
+                context = context.replaceAll("\\{\\{ \\.CitrusVersion }}", 
CitrusVersion.version());
+                context = context.replaceAll("\\{\\{ \\.CamelVersion }}", new 
VersionHelper().getVersion());
+
+                ExportHelper.safeCopy(new 
ByteArrayInputStream(context.getBytes(StandardCharsets.UTF_8)),
+                        citrusApplicationProperties);
+            } catch (Exception e) {
+                getMain().getOut().println("Failed to create %s for tests in: 
%s"
+                        
.formatted(CitrusSettings.getApplicationPropertiesFile(), 
citrusApplicationProperties));
+            }
+        }
+
+        return 0;
+    }
+
+    static class FileConsumer extends ParameterConsumer<TestInit> {
+        @Override
+        protected void doConsumeParameters(Stack<String> args, TestInit cmd) {
+            cmd.file = args.pop();
+        }
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
index 59a860268e7d..19d70e68b6bc 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
@@ -16,30 +16,21 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.test;
 
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Optional;
 
-import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.catalog.VersionHelper;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
-import org.apache.camel.dsl.jbang.core.commands.ExportHelper;
 import org.apache.camel.dsl.jbang.core.common.CamelJBangPlugin;
 import org.apache.camel.dsl.jbang.core.common.Plugin;
 import org.apache.camel.dsl.jbang.core.common.PluginExporter;
 import org.apache.camel.dsl.jbang.core.common.Printer;
-import org.apache.camel.util.IOHelper;
-import org.citrusframework.CitrusSettings;
 import org.citrusframework.CitrusVersion;
 import org.citrusframework.jbang.CitrusJBangMain;
 import org.citrusframework.jbang.commands.Agent;
 import org.citrusframework.jbang.commands.AgentRun;
 import org.citrusframework.jbang.commands.AgentStart;
 import org.citrusframework.jbang.commands.AgentStop;
-import org.citrusframework.jbang.commands.Init;
+import org.citrusframework.jbang.commands.CitrusCommand;
 import org.citrusframework.jbang.commands.Inspect;
 import org.citrusframework.jbang.commands.ListTests;
 import org.citrusframework.jbang.commands.Run;
@@ -48,13 +39,15 @@ import picocli.CommandLine;
 @CamelJBangPlugin(name = "camel-jbang-plugin-test", firstVersion = "4.14.0")
 public class TestPlugin implements Plugin {
 
+    public static final String TEST_DIR = "test";
+
     @Override
     public void customize(CommandLine commandLine, CamelJBangMain main) {
         CitrusJBangMain citrus = new CitrusJBangMain();
         citrus.withPrinter(new PipedPrinter(main.getOut()));
 
         var cmd = new CommandLine(new TestCommand(main))
-                .addSubcommand("init", new CommandLine(new Init(citrus)))
+                .addSubcommand("init", new CommandLine(new TestInit(citrus)))
                 .addSubcommand("inspect", new CommandLine(new Inspect(citrus)))
                 .addSubcommand("run", new CommandLine(new Run(citrus)))
                 .addSubcommand("ps", new CommandLine(new ListTests(citrus)), 
"ls")
@@ -78,8 +71,6 @@ public class TestPlugin implements Plugin {
      */
     private record CitrusExecutionStrategy(CamelJBangMain main) implements 
CommandLine.IExecutionStrategy {
 
-        public static final String TEST_DIR = "test";
-
         @Override
         public int execute(CommandLine.ParseResult parseResult)
                 throws CommandLine.ExecutionException, 
CommandLine.ParameterException {
@@ -90,21 +81,9 @@ public class TestPlugin implements Plugin {
             }
 
             if (isCitrusCommand(parseResult)) {
-                String command = "";
-                if (parseResult.originalArgs().size() > 2) {
-                    command = parseResult.originalArgs().get(1);
-                } else if (parseResult.originalArgs().size() == 2) {
-                    command = parseResult.originalArgs().get(1);
-                }
-
                 System.setProperty("citrus.jbang.version", 
CitrusVersion.version());
                 System.setProperty("citrus.camel.jbang.version", new 
VersionHelper().getVersion());
 
-                // Prepare commands
-                if ("init".equals(command)) {
-                    prepareInitCommand();
-                }
-
                 if (!isCamelLauncherRuntime()) {
                     var tccLoader = 
Thread.currentThread().getContextClassLoader();
                     try {
@@ -122,49 +101,6 @@ public class TestPlugin implements Plugin {
             return new CommandLine.RunLast().execute(parseResult);
         }
 
-        /**
-         * Prepare init command. Automatically uses test subfolder as a 
working directory for creating new tests.
-         * Automatically adds a citrus-application.properties configuration if 
not present.
-         */
-        private void prepareInitCommand() {
-            Path currentDir = Paths.get(".");
-            Path workingDir;
-            // Automatically set test subfolder as a working directory
-            if (TEST_DIR.equals(currentDir.getFileName().toString())) {
-                // current directory is already the test subfolder
-                workingDir = currentDir;
-            } else if (currentDir.resolve(TEST_DIR).toFile().exists()) {
-                // navigate to existing test subfolder
-                workingDir = currentDir.resolve(TEST_DIR);
-                System.setProperty(CitrusSettings.RESOURCES_WORKDIR_PROPERTY, 
workingDir.toString());
-            } else if (currentDir.resolve(TEST_DIR).toFile().mkdirs()) {
-                // create test subfolder and navigate to it
-                workingDir = currentDir.resolve(TEST_DIR);
-                System.setProperty(CitrusSettings.RESOURCES_WORKDIR_PROPERTY, 
workingDir.toString());
-            } else {
-                throw new RuntimeCamelException("Cannot create test working 
directory in: " + currentDir);
-            }
-
-            // Create Citrus application properties if not present
-            if 
(!workingDir.resolve(CitrusSettings.getApplicationPropertiesFile()).toFile().exists())
 {
-                Path citrusApplicationProperties = 
workingDir.resolve(CitrusSettings.getApplicationPropertiesFile());
-                try (InputStream is
-                        = TestPlugin.class.getClassLoader()
-                                
.getResourceAsStream("templates/citrus-application-properties.tmpl")) {
-                    String context = IOHelper.loadText(is);
-
-                    context = context.replaceAll("\\{\\{ \\.CitrusVersion }}", 
CitrusVersion.version());
-                    context = context.replaceAll("\\{\\{ \\.CamelVersion }}", 
new VersionHelper().getVersion());
-
-                    ExportHelper.safeCopy(new 
ByteArrayInputStream(context.getBytes(StandardCharsets.UTF_8)),
-                            citrusApplicationProperties);
-                } catch (Exception e) {
-                    main.getOut().println("Failed to create %s for tests in: 
%s"
-                            
.formatted(CitrusSettings.getApplicationPropertiesFile(), 
citrusApplicationProperties));
-                }
-            }
-        }
-
         /**
          * Evaluate if the current runtime is using Camel Launcher. Camel 
launcher sets a System property marking the
          * runtime nature. If the System property is not present we can assume 
that the runtime is something different
@@ -184,7 +120,7 @@ public class TestPlugin implements Plugin {
             }
 
             Object commandObject = subcommand.commandSpec().userObject();
-            return 
commandObject.getClass().getName().startsWith("org.citrusframework");
+            return commandObject instanceof CitrusCommand;
         }
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-test/src/test/java/org/apache/camel/dsl/jbang/core/commands/test/TestPluginTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/test/java/org/apache/camel/dsl/jbang/core/commands/test/TestPluginTest.java
new file mode 100644
index 000000000000..086c39ff2c5b
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/test/java/org/apache/camel/dsl/jbang/core/commands/test/TestPluginTest.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.dsl.jbang.core.commands.test;
+
+import java.nio.file.Path;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.camel.dsl.jbang.core.common.Printer;
+import org.apache.camel.dsl.jbang.core.common.StringPrinter;
+import org.apache.camel.dsl.jbang.core.common.VersionHelper;
+import org.citrusframework.spi.Resources;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine;
+
+class TestPluginTest {
+
+    private StringPrinter printer;
+
+    @BeforeEach
+    public void setup() {
+        // Set Camel version with system property value, usually set via Maven 
surefire plugin
+        // In case you run this test via local Java IDE you need to provide 
the system property or a default value here
+        VersionHelper.setCamelVersion(System.getProperty("camel.version", ""));
+        printer = new StringPrinter();
+        CommandLineHelper.useHomeDir("target");
+    }
+
+    @Test
+    public void shouldListTests() {
+        CamelJBangMain camelJBangMain = createCamelJBangMain();
+
+        camelJBangMain.execute("test", "ps");
+
+        Assertions.assertTrue(printer.getOutput().contains("PID  NAME  STATUS  
AGE"));
+    }
+
+    @Test
+    public void shouldInitYamlTest() {
+        Path targetDir = 
CommandLineHelper.getHomeDir().toAbsolutePath().resolve(TestPlugin.TEST_DIR);
+
+        CamelJBangMain camelJBangMain = createCamelJBangMain();
+        camelJBangMain.execute("test", "init", "sample.citrus.it.yaml",
+                "--directory", targetDir.toString());
+
+        
Assertions.assertTrue(targetDir.resolve("citrus-application.properties").toFile().exists());
+        
Assertions.assertTrue(targetDir.resolve("sample.citrus.it.yaml").toFile().exists());
+    }
+
+    @Test
+    public void shouldInitXmlTest() {
+        Path targetDir = 
CommandLineHelper.getHomeDir().toAbsolutePath().resolve(TestPlugin.TEST_DIR);
+
+        CamelJBangMain camelJBangMain = createCamelJBangMain();
+        camelJBangMain.execute("test", "init", "sample.citrus.it.xml",
+                "--directory", targetDir.toString());
+
+        
Assertions.assertTrue(targetDir.resolve("citrus-application.properties").toFile().exists());
+        
Assertions.assertTrue(targetDir.resolve("sample.citrus.it.xml").toFile().exists());
+    }
+
+    @Test
+    public void shouldRunTest() {
+        Path targetDir = 
CommandLineHelper.getHomeDir().toAbsolutePath().resolve(TestPlugin.TEST_DIR);
+
+        CamelJBangMain camelJBangMain = createCamelJBangMain();
+        camelJBangMain.execute("test", "init", "sample.citrus.it.yaml",
+                "--directory", targetDir.toString());
+
+        
Assertions.assertTrue(targetDir.resolve("citrus-application.properties").toFile().exists());
+        
Assertions.assertTrue(targetDir.resolve("sample.citrus.it.yaml").toFile().exists());
+
+        camelJBangMain.execute("test", "run", targetDir.toString());
+    }
+
+    @Test
+    public void shouldInspectTest() {
+        Path source = 
Resources.fromClasspath("my-sample.citrus.it.yaml").file().toPath();
+
+        CamelJBangMain camelJBangMain = createCamelJBangMain();
+        camelJBangMain.execute("test", "inspect", source.toString());
+
+        Assertions.assertEquals("""
+                {
+                  "name": "my-sample.citrus.it.yaml",
+                  "actions": [
+                    "echo"
+                  ]
+                }
+                """.trim(), printer.getOutput().trim());
+    }
+
+    /**
+     * Creates CamelJBangMain instance ready for unit testing. Automatically 
adds test plugin commands. Automatically
+     * sets the out printer.
+     */
+    private CamelJBangMain createCamelJBangMain() {
+        return new CamelJBangMain() {
+            @Override
+            public void postAddCommands(CommandLine commandLine, String[] 
args) {
+                TestPlugin plugin = new TestPlugin();
+                plugin.customize(commandLine, this);
+
+                super.postAddCommands(commandLine, args);
+            }
+
+            @Override
+            public void quit(int exitCode) {
+                if (exitCode < 0) {
+                    throw new RuntimeException("Unexpected exit code: " + 
exitCode);
+                }
+            }
+
+            @Override
+            public Printer getOut() {
+                return printer;
+            }
+        };
+    }
+
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-test/src/test/resources/my-sample.citrus.it.yaml
 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/test/resources/my-sample.citrus.it.yaml
new file mode 100644
index 000000000000..cd6a65a9c06d
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-test/src/test/resources/my-sample.citrus.it.yaml
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+name: my-sample.citrus.it
+author: Citrus
+status: FINAL
+description: Sample test in YAML
+variables:
+  - name: message
+    value: Citrus rocks!
+actions:
+  - echo:
+      message: "${message}"

Reply via email to