diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml b/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml index b660622ff8ea7..593ee0288eeef 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml +++ b/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml @@ -49,7 +49,7 @@ org.citrusframework - citrus-jbang-connector + citrus-base ${citrus-version} 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 0000000000000..7bc2aedbabddf --- /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,158 @@ +/* + * 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.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.commands.ExportHelper; +import org.apache.camel.util.IOHelper; +import org.citrusframework.CitrusVersion; +import picocli.CommandLine; + +/** + * Initializes a new Citrus test file from a template. Creates the test subfolder if it does not exist and writes a + * template test source for the given file type. Also creates a jbang.properties file with default Citrus dependencies + * when not already present. + */ +@CommandLine.Command(name = "init", + description = "Initialize a new Citrus test from a template") +public class TestInit extends CamelCommand { + + public static final String TEST_DIR = "test"; + + @CommandLine.Parameters(index = "0", description = "Test file name (e.g. MyTest.yaml, MyTest.xml, MyTest.java)") + String file; + + @CommandLine.Option(names = { "-d", "--directory" }, description = "Target directory", defaultValue = ".") + String directory; + + public TestInit(CamelJBangMain main) { + super(main); + } + + @Override + public Integer doCall() throws Exception { + // Determine file extension and base name + String ext = getFileExtension(file); + String baseName = getBaseName(file); + + if (ext.isEmpty()) { + printer().println("Cannot determine file type for: " + file); + return 1; + } + + // Load template for the file type + String template; + try (InputStream is = TestInit.class.getClassLoader().getResourceAsStream("templates/citrus-" + ext + ".tmpl")) { + if (is == null) { + printer().println("Unsupported test file type: " + ext); + return 1; + } + template = IOHelper.loadText(is); + } + + // Replace template placeholders + template = template.replaceAll("\\{\\{ \\.Name }}", baseName); + + // Determine the working directory + Path workingDir = resolveTestDir(); + if (workingDir == null) { + printer().println("Cannot create test working directory"); + return 1; + } + + // Create target directory if specified + Path targetDir; + if (".".equals(directory)) { + targetDir = workingDir; + } else { + targetDir = workingDir.resolve(directory); + Files.createDirectories(targetDir); + } + + // Create jbang properties with default dependencies if not present + createJBangProperties(workingDir); + + // Write the test file + Path testFile = targetDir.resolve(file); + Files.writeString(testFile, template); + + printer().println("Created test file: " + testFile); + return 0; + } + + /** + * Resolves and creates the test directory. Automatically uses the test subfolder as the working directory. + */ + private Path resolveTestDir() { + Path currentDir = Paths.get("."); + Path workingDir; + if (TEST_DIR.equals(currentDir.toAbsolutePath().normalize().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); + } else if (currentDir.resolve(TEST_DIR).toFile().mkdirs()) { + // create test subfolder and navigate to it + workingDir = currentDir.resolve(TEST_DIR); + } else { + return null; + } + return workingDir; + } + + /** + * Creates jbang.properties with default Citrus dependencies if not already present. + */ + private void createJBangProperties(Path workingDir) { + if (!workingDir.resolve("jbang.properties").toFile().exists()) { + Path jbangProperties = workingDir.resolve("jbang.properties"); + try (InputStream is + = TestInit.class.getClassLoader().getResourceAsStream("templates/jbang-properties.tmpl")) { + String context = IOHelper.loadText(is); + context = context.replaceAll("\\{\\{ \\.CitrusVersion }}", CitrusVersion.version()); + ExportHelper.safeCopy(new ByteArrayInputStream(context.getBytes(StandardCharsets.UTF_8)), jbangProperties); + } catch (Exception e) { + printer().println("Failed to create jbang.properties for tests in: " + jbangProperties); + } + } + } + + private static String getFileExtension(String fileName) { + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex > 0 && dotIndex < fileName.length() - 1) { + return fileName.substring(dotIndex + 1); + } + return ""; + } + + private static String getBaseName(String fileName) { + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex > 0) { + return fileName.substring(0, dotIndex); + } + return fileName; + } +} 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 a556df43b7c31..085fd9cb294df 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,27 +16,12 @@ */ 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.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Optional; -import org.apache.camel.RuntimeCamelException; 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.util.IOHelper; -import org.citrusframework.CitrusVersion; -import org.citrusframework.jbang.JBangSettings; -import org.citrusframework.jbang.JBangSupport; -import org.citrusframework.jbang.ProcessAndOutput; import picocli.CommandLine; @CamelJBangPlugin(name = "camel-jbang-plugin-test", firstVersion = "4.14.0") @@ -44,138 +29,15 @@ public class TestPlugin implements Plugin { @Override public void customize(CommandLine commandLine, CamelJBangMain main) { - commandLine.setExecutionStrategy(new CitrusExecutionStrategy(main)) - .addSubcommand("test", new CommandLine(new TestCommand(main)) - .setUnmatchedArgumentsAllowed(true) - .setUnmatchedOptionsAllowedAsOptionParameters(true)); + var cmd = new CommandLine(new TestCommand(main)) + .addSubcommand("init", new CommandLine(new TestInit(main))) + .addSubcommand("run", new CommandLine(new TestRun(main))); + + commandLine.addSubcommand("test", cmd); } @Override public Optional getExporter() { return Optional.of(new TestPluginExporter()); } - - /** - * Command execution strategy delegates to Citrus JBang for subcommands like init or run. Performs special command - * preparations and makes sure to run the proper Citrus version for this Camel release. - * - * @param main Camel JBang main that provides the output printer. - */ - 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 { - - String command; - List args = Collections.emptyList(); - - if (parseResult.originalArgs().size() > 2) { - command = parseResult.originalArgs().get(1); - args = parseResult.originalArgs().subList(2, parseResult.originalArgs().size()); - } else if (parseResult.originalArgs().size() == 2) { - command = parseResult.originalArgs().get(1); - } else { - // run help command by default - command = "--help"; - } - - JBangSupport citrus = JBangSupport.jbang().app(JBangSettings.getApp()) - .withSystemProperty("citrus.jbang.version", CitrusVersion.version()); - - // Prepare commands - if ("init".equals(command)) { - return executeInitCommand(citrus, args); - } else if ("run".equals(command)) { - return executeRunCommand(citrus, args); - } - - return execute(citrus, command, args); - } - - /** - * Prepare and execute init command. Automatically uses test subfolder as a working directory for creating new - * tests. Automatically adds a jbang.properties configuration to add required Camel Citrus dependencies. - */ - private int executeInitCommand(JBangSupport citrus, List args) { - 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); - citrus.workingDir(workingDir); - } else if (currentDir.resolve(TEST_DIR).toFile().mkdirs()) { - // create test subfolder and navigate to it - workingDir = currentDir.resolve(TEST_DIR); - citrus.workingDir(workingDir); - } else { - throw new RuntimeCamelException("Cannot create test working directory in: " + currentDir); - } - - // Create jbang properties with default dependencies if not present - if (!workingDir.resolve("jbang.properties").toFile().exists()) { - Path jbangProperties = workingDir.resolve("jbang.properties"); - try (InputStream is - = TestPlugin.class.getClassLoader().getResourceAsStream("templates/jbang-properties.tmpl")) { - String context = IOHelper.loadText(is); - - context = context.replaceAll("\\{\\{ \\.CitrusVersion }}", CitrusVersion.version()); - - ExportHelper.safeCopy(new ByteArrayInputStream(context.getBytes(StandardCharsets.UTF_8)), jbangProperties); - } catch (Exception e) { - main.getOut().println("Failed to create jbang.properties for tests in:" + jbangProperties); - } - } - - return execute(citrus, "init", args); - } - - /** - * Prepare and execute Citrus run command. Automatically navigates to test subfolder if it is present and uses - * this as a working directory. Runs command asynchronous streaming logs to the main output of this Camel JBang - * process. - */ - private int executeRunCommand(JBangSupport citrus, List args) { - Path currentDir = Paths.get("."); - List runArgs = new ArrayList<>(args); - // automatically navigate to test subfolder for test execution - if (currentDir.resolve(TEST_DIR).toFile().exists()) { - // set test subfolder as working directory - citrus.workingDir(currentDir.resolve(TEST_DIR)); - - // remove test folder prefix in test file path if present - if (!args.isEmpty() && args.get(0).startsWith(TEST_DIR + "/")) { - runArgs = new ArrayList<>(args.subList(1, args.size())); - runArgs.add(0, args.get(0).substring((TEST_DIR + "/").length())); - } - } - - citrus.withOutputListener(output -> main.getOut().print(output)); - ProcessAndOutput pao = citrus.runAsync("run", runArgs); - try { - pao.waitFor(); - } catch (InterruptedException e) { - main.getOut().printErr("Interrupted while running Citrus command", e); - } - - return pao.getProcess().exitValue(); - } - - /** - * Uses given Citrus JBang instance to run the given command using the given arguments. - * - * @return exit code of the command process. - */ - private int execute(JBangSupport citrus, String command, List args) { - ProcessAndOutput pao = citrus.run(command, args); - main.getOut().print(pao.getOutput()); - return pao.getProcess().exitValue(); - } - } } diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestRun.java b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestRun.java new file mode 100644 index 0000000000000..6d1094ada2824 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestRun.java @@ -0,0 +1,229 @@ +/* + * 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.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.citrusframework.TestSource; +import org.citrusframework.main.TestEngine; +import org.citrusframework.main.TestRunConfiguration; +import org.citrusframework.util.FileUtils; +import picocli.CommandLine; + +/** + * Runs Citrus test files directly using the Citrus test engine. This replaces the indirect JBang process call with a + * direct in-process test execution. + */ +@CommandLine.Command(name = "run", + description = "Run Citrus tests") +public class TestRun extends CamelCommand { + + public static final String TEST_DIR = "test"; + public static final String WORK_DIR = ".citrus-jbang"; + + private static final String[] ACCEPTED_FILE_EXT = { "xml", "yaml", "yml", "java", "groovy", "feature" }; + + @CommandLine.Parameters(description = "Test files or directories to run", arity = "0..*") + String[] files; + + @CommandLine.Option(names = { "--engine" }, description = "Test engine to use", defaultValue = "default") + String engine; + + @CommandLine.Option(names = { "--verbose" }, description = "Enable verbose output") + boolean verbose; + + @CommandLine.Option(names = { "-p", "--property" }, description = "Set system properties (key=value)") + String[] properties; + + @CommandLine.Option(names = { "--includes" }, description = "Test name include patterns") + String[] includes; + + public TestRun(CamelJBangMain main) { + super(main); + } + + @Override + public Integer doCall() throws Exception { + Path currentDir = Paths.get("."); + + // Determine working directory (prefer test subfolder) + Path workDir; + if (currentDir.resolve(TEST_DIR).toFile().exists()) { + workDir = currentDir.resolve(TEST_DIR); + } else { + workDir = currentDir; + } + + // Set up system properties + Map props = new HashMap<>(); + if (properties != null) { + for (String prop : properties) { + String[] parts = prop.split("=", 2); + if (parts.length == 2) { + props.put(parts[0], parts[1]); + } + } + } + + // Apply system properties + for (Map.Entry entry : props.entrySet()) { + System.setProperty(entry.getKey(), entry.getValue()); + } + + // Clean and create work directory + File citrusWorkDir = new File(WORK_DIR); + removeDir(citrusWorkDir); + if (!citrusWorkDir.mkdirs()) { + printer().println("Failed to create working directory " + WORK_DIR); + return 1; + } + + // Resolve test sources + List testSources = new ArrayList<>(); + resolveTests(files, testSources, workDir); + + // If no explicit files given, scan current directory + if (testSources.isEmpty()) { + resolveTests(new String[] { workDir.toString() }, testSources, workDir); + } + + if (testSources.isEmpty()) { + printer().println("No test files found"); + return 1; + } + + // Build test run configuration + TestRunConfiguration configuration = getRunConfiguration(testSources, workDir); + + // Look up and run the test engine + TestEngine testEngine = TestEngine.lookup(configuration); + testEngine.run(); + + return 0; + } + + /** + * Creates a test run configuration from the resolved test sources. + */ + protected TestRunConfiguration getRunConfiguration(List testSources, Path workDir) { + String ext = FileUtils.getFileExtension(testSources.get(0)); + + TestRunConfiguration configuration = new TestRunConfiguration(); + configuration.setWorkDir(workDir.toAbsolutePath().toString()); + + if (!"default".equals(engine)) { + configuration.setEngine(engine); + } else if ("feature".equals(ext)) { + configuration.setEngine("cucumber"); + } + + configuration.setVerbose(verbose); + + if (includes != null) { + configuration.setIncludes(includes); + } + + // Add test sources + List sources = new ArrayList<>(); + for (String source : testSources) { + String sourceExt = FileUtils.getFileExtension(source); + String baseName = FileUtils.getBaseName(new File(source).getName()); + sources.add(new TestSource(sourceExt, baseName, source)); + } + configuration.setTestSources(sources); + + return configuration; + } + + /** + * Resolves test file paths from the given arguments. Handles both individual files and directories. + */ + private void resolveTests(String[] testArgs, List resolved, Path workDir) { + if (testArgs == null) { + return; + } + + for (String arg : testArgs) { + // Adjust path if it starts with test/ prefix and we're using the test subfolder + String filePath = arg; + if (filePath.startsWith(TEST_DIR + "/")) { + filePath = filePath.substring((TEST_DIR + "/").length()); + } + + File resolved0 = workDir.resolve(filePath).toFile(); + if (!resolved0.exists()) { + resolved0 = new File(filePath); + } + final File testFile = resolved0; + + if (testFile.isDirectory()) { + // Scan directory for test files + String[] dirFiles = testFile.list(); + if (dirFiles != null) { + String[] fullPaths = Arrays.stream(dirFiles) + .filter(f -> !skipFile(f)) + .map(f -> new File(testFile, f).getPath()) + .toArray(String[]::new); + resolveTests(fullPaths, resolved, workDir); + } + } else if (testFile.exists() && !skipFile(testFile.getName())) { + resolved.add(testFile.getPath()); + } + } + } + + /** + * Checks if a file should be skipped based on its extension. + */ + private boolean skipFile(String fileName) { + if (fileName.startsWith(".")) { + return true; + } + String ext = FileUtils.getFileExtension(fileName); + return Arrays.stream(ACCEPTED_FILE_EXT).noneMatch(e -> e.equals(ext)); + } + + /** + * Recursively removes a directory. + */ + private static void removeDir(File dir) { + if (dir.exists()) { + delete(dir); + } + } + + private static void delete(File file) { + if (file.isDirectory()) { + File[] children = file.listFiles(); + if (children != null) { + for (File child : children) { + delete(child); + } + } + } + file.delete(); + } +} diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-feature.tmpl b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-feature.tmpl new file mode 100644 index 0000000000000..88c4e347d8a12 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-feature.tmpl @@ -0,0 +1,8 @@ +Feature: {{ .Name }} + + Background: + Given variables + | message | "Citrus rocks!" | + + Scenario: Print message + Then print '${message}' diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-groovy.tmpl b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-groovy.tmpl new file mode 100644 index 0000000000000..0acce60ab4ccc --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-groovy.tmpl @@ -0,0 +1,5 @@ +variables { + message = "Citrus rocks!" +} + +$(echo('${message}')) diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-java.tmpl b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-java.tmpl new file mode 100644 index 0000000000000..f99d9dbac01e3 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-java.tmpl @@ -0,0 +1,21 @@ +import org.citrusframework.TestActionSupport; +import org.citrusframework.TestCaseRunner; +import org.citrusframework.annotations.CitrusResource; + + +public class {{ .Name }} implements Runnable, TestActionSupport { + + @CitrusResource + TestCaseRunner t; + + @Override + public void run() { + t.given( + createVariables().variable("message", "Citrus rocks!") + ); + + t.then( + echo().message("${message}") + ); + } +} diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-xml.tmpl b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-xml.tmpl new file mode 100644 index 0000000000000..e394315b7f6b4 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-xml.tmpl @@ -0,0 +1,12 @@ + + Sample test in XML + + + + + + + + diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-yaml.tmpl b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-yaml.tmpl new file mode 100644 index 0000000000000..c88c446241158 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/resources/templates/citrus-yaml.tmpl @@ -0,0 +1,10 @@ +name: {{ .Name }} +author: Citrus +status: FINAL +description: Sample test in YAML +variables: + - name: message + value: Citrus rocks! +actions: + - echo: + message: "${message}" diff --git a/dsl/camel-jbang/camel-launcher/pom.xml b/dsl/camel-jbang/camel-launcher/pom.xml index dbb49c2668662..4d1cd8f45e45c 100644 --- a/dsl/camel-jbang/camel-launcher/pom.xml +++ b/dsl/camel-jbang/camel-launcher/pom.xml @@ -66,12 +66,16 @@ - org.apache.camel camel-jbang-plugin-generate ${project.version} + + org.apache.camel + camel-jbang-plugin-test + ${project.version} + org.apache.camel camel-jbang-plugin-kubernetes diff --git a/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java b/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java index 594c916bfb810..8acfc37760ae1 100644 --- a/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java +++ b/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java @@ -19,6 +19,7 @@ import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; import org.apache.camel.dsl.jbang.core.commands.generate.GeneratePlugin; import org.apache.camel.dsl.jbang.core.commands.kubernetes.KubernetesPlugin; +import org.apache.camel.dsl.jbang.core.commands.test.TestPlugin; import org.apache.camel.dsl.jbang.core.commands.validate.ValidatePlugin; import picocli.CommandLine; @@ -32,6 +33,7 @@ public void postAddCommands(CommandLine commandLine, String[] args) { // install embedded plugins new GeneratePlugin().customize(commandLine, this); new KubernetesPlugin().customize(commandLine, this); + new TestPlugin().customize(commandLine, this); new ValidatePlugin().customize(commandLine, this); }