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

apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 0fdb5e4f90 IGNITE-21828 Fix config update commands without parameters 
(#3550)
0fdb5e4f90 is described below

commit 0fdb5e4f9089d4c26298cc37a82d598292964fa4
Author: Vadim Pakhnushev <8614891+valep...@users.noreply.github.com>
AuthorDate: Thu Apr 4 11:10:40 2024 +0300

    IGNITE-21828 Fix config update commands without parameters (#3550)
---
 .../ignite/internal/cli/CliIntegrationTest.java    |   6 +
 .../internal/cli/commands/ItConfigCommandTest.java | 191 ---------------------
 .../configuration/ItConfigCommandTest.java         |  37 +++-
 .../cli/commands/SpacedParameterMixin.java         |   2 +-
 .../internal/cli/commands/CliCommandTestBase.java  |  48 ++++--
 .../internal/cli/commands/ProfileMixinTest.java    |  45 +----
 .../cluster/config/ConfigUpdateCommandTest.java    | 131 ++++++++++++++
 7 files changed, 213 insertions(+), 247 deletions(-)

diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/CliIntegrationTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/CliIntegrationTest.java
index 0740d85c51..eb13a09cb0 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/CliIntegrationTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/CliIntegrationTest.java
@@ -151,6 +151,12 @@ public abstract class CliIntegrationTest extends 
ClusterPerClassIntegrationTest
                 .contains(expectedOutput);
     }
 
+    protected void assertOutputDoesNotContain(String expectedOutput) {
+        assertThat(sout.toString())
+                .as("Expected command output to not contain: " + 
expectedOutput + " but was " + sout.toString())
+                .doesNotContain(expectedOutput);
+    }
+
     protected void assertOutputMatches(String regex) {
         assertThat(sout.toString())
                 .as("Expected command output to match regex: " + regex + " but 
it is not: " + sout.toString())
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItConfigCommandTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItConfigCommandTest.java
deleted file mode 100644
index a8046dca15..0000000000
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItConfigCommandTest.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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.ignite.internal.cli.commands;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.ignite.internal.cli.core.style.AnsiStringSupport.fg;
-import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
-import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
-import java.nio.file.Path;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgnitionManager;
-import org.apache.ignite.InitParameters;
-import org.apache.ignite.internal.app.IgniteImpl;
-import org.apache.ignite.internal.cli.AbstractCliTest;
-import org.apache.ignite.internal.cli.core.style.AnsiStringSupport.Color;
-import org.apache.ignite.internal.testframework.TestIgnitionManager;
-import org.apache.ignite.internal.testframework.WorkDirectory;
-import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInfo;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-/**
- * Integration test for {@code ignite node/cluster config} commands.
- */
-@ExtendWith(WorkDirectoryExtension.class)
-public class ItConfigCommandTest extends AbstractCliTest {
-    /** Node. */
-    private IgniteImpl node;
-
-    @BeforeEach
-    void setup(@WorkDirectory Path workDir, TestInfo testInfo) {
-        String nodeName = testNodeName(testInfo, 0);
-
-        CompletableFuture<Ignite> future = TestIgnitionManager.start(nodeName, 
null, workDir);
-
-        InitParameters initParameters = InitParameters.builder()
-                .destinationNodeName(nodeName)
-                .metaStorageNodeNames(List.of(nodeName))
-                .clusterName("cluster")
-                .build();
-
-        TestIgnitionManager.init(initParameters);
-
-        assertThat(future, willCompleteSuccessfully());
-
-        node = (IgniteImpl) future.join();
-    }
-
-    @AfterEach
-    void tearDown(TestInfo testInfo) {
-        IgnitionManager.stop(testNodeName(testInfo, 0));
-    }
-
-    @Test
-    public void setAndGetWithManualHost() {
-        int exitCode = execute(
-                "node",
-                "config",
-                "update",
-                "--node-url",
-                "http://localhost:"; + node.restHttpAddress().port(),
-                "network.shutdownQuietPeriod=1"
-        );
-
-        assertEquals(0, exitCode);
-        assertThat(
-                out.toString(UTF_8),
-                containsString("Node configuration updated. "
-                        + fg(Color.YELLOW).mark("Restart the node to apply 
changes."))
-
-        );
-
-        resetStreams();
-
-        exitCode = execute(
-                "node",
-                "config",
-                "show",
-                "--node-url",
-                "http://localhost:"; + node.restHttpAddress().port()
-        );
-
-        assertEquals(0, exitCode);
-
-        assertThat(
-                out.toString(UTF_8),
-                containsString("\"shutdownQuietPeriod\" : 1")
-        );
-    }
-
-    @Test
-    public void setWithWrongData() {
-        int exitCode = execute(
-                "node",
-                "config",
-                "update",
-                "--node-url",
-                "http://localhost:"; + node.restHttpAddress().port(),
-                "network.foo=\"bar\""
-        );
-
-        assertEquals(1, exitCode);
-        assertThat(
-                err.toString(UTF_8),
-                containsString("'network' configuration doesn't have the 'foo' 
sub-configuration")
-        );
-
-        resetStreams();
-
-        exitCode = execute(
-                "node",
-                "config",
-                "update",
-                "--node-url",
-                "http://localhost:"; + node.restHttpAddress().port(),
-                "network.shutdownQuietPeriod=asd"
-        );
-
-        assertEquals(1, exitCode);
-        assertThat(
-                err.toString(UTF_8),
-                containsString("'long' is expected as a type for the 
'network.shutdownQuietPeriod' configuration value")
-        );
-    }
-
-    @Test
-    public void partialGet() {
-        int exitCode = execute(
-                "node",
-                "config",
-                "show",
-                "--node-url",
-                "http://localhost:"; + node.restHttpAddress().port(),
-                "network"
-        );
-
-        assertEquals(0, exitCode);
-
-        assertThat(
-                out.toString(UTF_8),
-                containsString("\"inbound\"")
-        );
-
-        assertFalse(out.toString(UTF_8).contains("\"node\""));
-    }
-
-    /**
-     * Unescapes quotes in the input string.
-     *
-     * @param input String.
-     * @return String with unescaped quotes.
-     */
-    private static String unescapeQuotes(String input) {
-        return input.replace("\\\"", "\"");
-    }
-
-    /**
-     * Removes trailing quotes from the input string.
-     *
-     * @param input String.
-     * @return String without trailing quotes.
-     */
-    private static String removeTrailingQuotes(String input) {
-        return input.substring(1, input.length() - 1);
-    }
-}
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
index 7eced9cf6e..24a33ac214 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
@@ -18,9 +18,11 @@
 package org.apache.ignite.internal.cli.commands.configuration;
 
 
+import static org.apache.ignite.internal.cli.core.style.AnsiStringSupport.fg;
 import static org.junit.jupiter.api.Assertions.assertAll;
 
 import org.apache.ignite.internal.cli.CliIntegrationTest;
+import org.apache.ignite.internal.cli.core.style.AnsiStringSupport.Color;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
@@ -77,7 +79,8 @@ class ItConfigCommandTest extends CliIntegrationTest {
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertErrOutputIsEmpty,
-                this::assertOutputIsNotEmpty
+                () -> assertOutputContains("Node configuration updated. "
+                        + fg(Color.YELLOW).mark("Restart the node to apply 
changes."))
         );
 
         // When read the updated cluster configuration
@@ -196,4 +199,36 @@ class ItConfigCommandTest extends CliIntegrationTest {
                 this::assertOutputIsNotEmpty
         );
     }
+
+    @Test
+    void updateWithWrongData() {
+        execute("node", "config", "update", "--node-url", NODE_URL, 
"network.foo=\"bar\"");
+
+        assertAll(
+                () -> assertExitCodeIs(1),
+                () -> assertErrOutputContains("'network' configuration doesn't 
have the 'foo' sub-configuration"),
+                this::assertOutputIsEmpty
+        );
+
+        resetOutput();
+
+        execute("node", "config", "update", "--node-url", NODE_URL, 
"network.shutdownQuietPeriod=asd");
+
+        assertAll(
+                () -> assertExitCodeIs(1),
+                () -> assertErrOutputContains("'long' is expected as a type 
for the 'network.shutdownQuietPeriod' configuration value"),
+                this::assertOutputIsEmpty
+        );
+    }
+
+    @Test
+    public void partialGet() {
+        execute("node", "config", "show", "--node-url", NODE_URL, "network");
+        assertAll(
+                this::assertExitCodeIsZero,
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("\"inbound\""),
+                () -> assertOutputDoesNotContain("\"node\"")
+        );
+    }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/SpacedParameterMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/SpacedParameterMixin.java
index 3ab058095e..c9c6b714e7 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/SpacedParameterMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/SpacedParameterMixin.java
@@ -26,7 +26,7 @@ import picocli.CommandLine.Parameters;
  * With this mixin, we allow the user to specify one parameter with spaces 
without quotation.
  */
 public class SpacedParameterMixin {
-    @Parameters(index = "0..*")
+    @Parameters(arity = "1")
     private String[] args;
 
     @Override
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
index 3e5ee65bf4..19994ab7a6 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
@@ -18,6 +18,10 @@
 package org.apache.ignite.internal.cli.commands;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import io.micronaut.configuration.picocli.MicronautFactory;
 import io.micronaut.context.ApplicationContext;
@@ -26,38 +30,27 @@ import jakarta.inject.Inject;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Arrays;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.ignite.internal.cli.core.call.Call;
+import org.apache.ignite.internal.cli.core.call.CallInput;
+import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.mockito.ArgumentCaptor;
 import picocli.CommandLine;
 
 /**
  * Base class for testing CLI commands.
  */
 @MicronautTest
-public abstract class CliCommandTestBase {
+public abstract class CliCommandTestBase extends BaseIgniteAbstractTest {
     @Inject
     private ApplicationContext context;
 
-    @Inject
-    private NodeNameRegistry nodeNameRegistry;
-
-    private CommandLine cmd;
-
     private StringWriter sout;
 
     private StringWriter serr;
 
     private int exitCode = Integer.MIN_VALUE;
 
-    @BeforeEach
-    public void setUp() {
-        cmd = new CommandLine(getCommandClass(), new 
MicronautFactory(context));
-        sout = new StringWriter();
-        serr = new StringWriter();
-        cmd.setOut(new PrintWriter(sout));
-        cmd.setErr(new PrintWriter(serr));
-    }
-
     protected abstract Class<?> getCommandClass();
 
     protected void execute(String argsLine) {
@@ -65,6 +58,14 @@ public abstract class CliCommandTestBase {
     }
 
     protected void execute(String... args) {
+        // Create command just before execution as some tests could register 
singletons which should be used by the command
+        CommandLine cmd = new CommandLine(getCommandClass(), new 
MicronautFactory(context));
+
+        sout = new StringWriter();
+        serr = new StringWriter();
+        cmd.setOut(new PrintWriter(sout));
+        cmd.setErr(new PrintWriter(serr));
+
         exitCode = cmd.execute(args);
     }
 
@@ -125,4 +126,17 @@ public abstract class CliCommandTestBase {
                 .as("Expected command error output to contain: " + 
expectedErrOutput + " but was " + serr.toString())
                 .contains(expectedErrOutput);
     }
+
+    protected <IT extends CallInput, OT, T extends Call<IT, OT>> T 
registerMockCall(Class<T> callClass) {
+        T mock = mock(callClass);
+        context.registerSingleton(mock);
+        when(mock.execute(any())).thenReturn(DefaultCallOutput.empty());
+        return mock;
+    }
+
+    protected static <IT extends CallInput, OT, T extends Call<IT, OT>> IT 
verifyCallInput(T call, Class<IT> inputClass) {
+        ArgumentCaptor<IT> captor = ArgumentCaptor.forClass(inputClass);
+        verify(call).execute(captor.capture());
+        return captor.getValue();
+    }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/ProfileMixinTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/ProfileMixinTest.java
index 039e053c87..0e50d92139 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/ProfileMixinTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/ProfileMixinTest.java
@@ -19,15 +19,7 @@ package org.apache.ignite.internal.cli.commands;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.params.provider.Arguments.arguments;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import io.micronaut.configuration.picocli.MicronautFactory;
-import io.micronaut.context.ApplicationContext;
-import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
-import jakarta.inject.Inject;
+
 import java.util.function.Function;
 import java.util.stream.Stream;
 import org.apache.ignite.internal.cli.call.cluster.ClusterInitCall;
@@ -45,21 +37,16 @@ import 
org.apache.ignite.internal.cli.call.configuration.NodeConfigUpdateCallInp
 import org.apache.ignite.internal.cli.call.node.status.NodeStatusCall;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallInput;
-import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
-import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
-import org.mockito.ArgumentCaptor;
-import picocli.CommandLine;
 
 /**
  * Test for --profile override for --node-url and --cluster-endpoint-url 
options.
  */
-@MicronautTest
-public class ProfileMixinTest extends BaseIgniteAbstractTest {
+public class ProfileMixinTest extends CliCommandTestBase {
     /**
      * Cluster URL from default profile in integration_tests.ini.
      */
@@ -75,9 +62,6 @@ public class ProfileMixinTest extends BaseIgniteAbstractTest {
      */
     private static final String URL_FROM_CMD = "http://localhost:10302";;
 
-    @Inject
-    private ApplicationContext ctx;
-
     @ParameterizedTest
     @DisplayName("Should take URL from default profile")
     @MethodSource("allCallsProvider")
@@ -162,7 +146,7 @@ public class ProfileMixinTest extends 
BaseIgniteAbstractTest {
         assertEquals(URL_FROM_CMD, urlSupplier.apply(callInput));
     }
 
-    static Stream<Arguments> nodeCallsProvider() {
+    private static Stream<Arguments> nodeCallsProvider() {
         return Stream.of(
                 arguments(
                         "node config show",
@@ -185,7 +169,7 @@ public class ProfileMixinTest extends 
BaseIgniteAbstractTest {
         );
     }
 
-    static Stream<Arguments> clusterCallsProvider() {
+    private static Stream<Arguments> clusterCallsProvider() {
         return Stream.of(
                 arguments(
                         "cluster config show",
@@ -220,25 +204,12 @@ public class ProfileMixinTest extends 
BaseIgniteAbstractTest {
         );
     }
 
-    static Stream<Arguments> allCallsProvider() {
+    private static Stream<Arguments> allCallsProvider() {
         return Stream.concat(nodeCallsProvider(), clusterCallsProvider());
     }
 
-    private void execute(String cmdLine) {
-        CommandLine cmd = new CommandLine(TopLevelCliCommand.class, new 
MicronautFactory(ctx));
-        cmd.execute(cmdLine.split(" "));
-    }
-
-    private <IT extends CallInput, OT, T extends Call<IT, OT>> T 
registerMockCall(Class<T> callClass) {
-        T mock = mock(callClass);
-        ctx.registerSingleton(mock);
-        when(mock.execute(any())).thenReturn(DefaultCallOutput.empty());
-        return mock;
-    }
-
-    private <IT extends CallInput, OT, T extends Call<IT, OT>> IT 
verifyCallInput(T call, Class<IT> inputClass) {
-        ArgumentCaptor<IT> captor = ArgumentCaptor.forClass(inputClass);
-        verify(call).execute(captor.capture());
-        return captor.getValue();
+    @Override
+    protected Class<?> getCommandClass() {
+        return TopLevelCliCommand.class;
     }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ConfigUpdateCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ConfigUpdateCommandTest.java
new file mode 100644
index 0000000000..7cd3d67ab3
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ConfigUpdateCommandTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.ignite.internal.cli.commands.cluster.config;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+import java.util.function.Function;
+import java.util.stream.Stream;
+import 
org.apache.ignite.internal.cli.call.configuration.ClusterConfigUpdateCall;
+import 
org.apache.ignite.internal.cli.call.configuration.ClusterConfigUpdateCallInput;
+import org.apache.ignite.internal.cli.call.configuration.NodeConfigUpdateCall;
+import 
org.apache.ignite.internal.cli.call.configuration.NodeConfigUpdateCallInput;
+import org.apache.ignite.internal.cli.commands.CliCommandTestBase;
+import org.apache.ignite.internal.cli.commands.TopLevelCliCommand;
+import org.apache.ignite.internal.cli.core.call.Call;
+import org.apache.ignite.internal.cli.core.call.CallInput;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class ConfigUpdateCommandTest extends CliCommandTestBase {
+    @Override
+    protected Class<?> getCommandClass() {
+        return TopLevelCliCommand.class;
+    }
+
+    private static Stream<Arguments> calls() {
+        return Stream.of(
+                arguments(
+                        "cluster config update",
+                        ClusterConfigUpdateCall.class,
+                        ClusterConfigUpdateCallInput.class,
+                        (Function<ClusterConfigUpdateCallInput, String>) 
ClusterConfigUpdateCallInput::getConfig
+                ),
+                arguments(
+                        "node config update",
+                        NodeConfigUpdateCall.class,
+                        NodeConfigUpdateCallInput.class,
+                        (Function<NodeConfigUpdateCallInput, String>) 
NodeConfigUpdateCallInput::getConfig
+                )
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("calls")
+    void noParameter(String command) {
+        // When executed without arguments
+        execute(command);
+
+        assertAll(
+                () -> assertExitCodeIs(2),
+                this::assertOutputIsEmpty,
+                () -> assertErrOutputContains("Missing required parameter: 
'<args>'")
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("calls")
+    <IT extends CallInput, OT, T extends Call<IT, OT>> void unquotedParameter(
+            String command,
+            Class<T> callClass,
+            Class<IT> callInputClass,
+            Function<IT, String> configFunction
+    ) {
+        checkParameters(command, callClass, callInputClass, configFunction, 
"test", "test");
+    }
+
+    @ParameterizedTest
+    @MethodSource("calls")
+    <IT extends CallInput, OT, T extends Call<IT, OT>> void quotedParameter(
+            String command,
+            Class<T> callClass,
+            Class<IT> callInputClass,
+            Function<IT, String> configFunction
+    ) {
+        checkParameters(command, callClass, callInputClass, configFunction, 
"\"test\"", "test");
+    }
+
+    @ParameterizedTest
+    @MethodSource("calls")
+    <IT extends CallInput, OT, T extends Call<IT, OT>> void unquotedParameters(
+            String command,
+            Class<T> callClass,
+            Class<IT> callInputClass,
+            Function<IT, String> configFunction
+    ) {
+        checkParameters(command, callClass, callInputClass, configFunction, 
"test1 test2", "test1 test2");
+    }
+
+    @ParameterizedTest
+    @MethodSource("calls")
+    <IT extends CallInput, OT, T extends Call<IT, OT>> void quotedParameters(
+            String command,
+            Class<T> callClass,
+            Class<IT> callInputClass,
+            Function<IT, String> configFunction
+    ) {
+        checkParameters(command, callClass, callInputClass, configFunction, 
"\"test1\" \"test2\"", "test1 test2");
+    }
+
+    private <IT extends CallInput, OT, T extends Call<IT, OT>> void 
checkParameters(
+            String command,
+            Class<T> callClass,
+            Class<IT> callInputClass,
+            Function<IT, String> configFunction,
+            String parameters,
+            String expected
+    ) {
+        T call = registerMockCall(callClass);
+        execute(command + " " + parameters);
+        IT callInput = verifyCallInput(call, callInputClass);
+        assertEquals(expected, configFunction.apply(callInput));
+    }
+}

Reply via email to