You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2022/12/19 06:20:50 UTC

[ignite-3] branch main updated: IGNITE-18221: Filter non-repitable options in interactive suggestions (#1432)

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

isapego 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 2ace66a2b4 IGNITE-18221: Filter non-repitable options in interactive suggestions (#1432)
2ace66a2b4 is described below

commit 2ace66a2b4842e1c2b2f66fa436801f4e9211658
Author: Ivan Gagarkin <ga...@gmail.com>
AuthorDate: Mon Dec 19 10:20:44 2022 +0400

    IGNITE-18221: Filter non-repitable options in interactive suggestions (#1432)
    
    Co-authored-by: Ivan Gagarkin <ig...@gridgain.com>
---
 .../cli/core/repl/completer/CompleterConf.java     |  1 +
 .../completer/DynamicCompleterActivationPoint.java |  1 +
 .../completer/{ => filter}/CompleterFilter.java    |  2 +-
 .../{ => filter}/DynamicCompleterFilter.java       |  2 +-
 .../{ => filter}/ExclusionsCompleterFilter.java    |  2 +-
 .../filter/NonRepeatableOptionsFilter.java         | 79 ++++++++++++++++++++++
 .../ShortOptionsFilter.java}                       | 19 ++++--
 .../core/repl/executor/IgnitePicocliCommands.java  | 18 +++--
 .../cli/core/repl/executor/ReplExecutor.java       | 11 ++-
 .../{ => filter}/DynamicCompleterFilterTest.java   |  3 +-
 .../ExclusionsCompleterFilterTest.java             |  3 +-
 .../filter/NonRepeatableOptionsFilterTest.java     | 57 ++++++++++++++++
 .../completer/filter/ShortOptionsFilterTest.java   | 46 +++++++++++++
 13 files changed, 220 insertions(+), 24 deletions(-)

diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterConf.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterConf.java
index d2840cde77..ca7b74c20f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterConf.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterConf.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.ignite.internal.cli.commands.Options;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.CompleterFilter;
 
 /**
  * Configuration for dynamic completer. It declares for what command and option the completer could be applied.
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
index 8f1ccce061..a2d8cdd5e1 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.cli.core.repl.completer;
 
 import jakarta.inject.Singleton;
 import org.apache.ignite.internal.cli.commands.Options;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.ExclusionsCompleterFilter;
 import org.apache.ignite.internal.cli.core.repl.completer.hocon.ClusterConfigDynamicCompleterFactory;
 import org.apache.ignite.internal.cli.core.repl.completer.hocon.NodeConfigDynamicCompleterFactory;
 import org.apache.ignite.internal.cli.core.repl.completer.node.NodeNameDynamicCompleterFactory;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterFilter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/CompleterFilter.java
similarity index 93%
rename from modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterFilter.java
rename to modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/CompleterFilter.java
index 6830f9712b..9e183fa48f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/CompleterFilter.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/CompleterFilter.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.core.repl.completer;
+package org.apache.ignite.internal.cli.core.repl.completer.filter;
 
 /**
  * Filters the result of completer.
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/DynamicCompleterFilter.java
similarity index 97%
rename from modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilter.java
rename to modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/DynamicCompleterFilter.java
index 59f5459438..bc2fe7f204 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilter.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/DynamicCompleterFilter.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.core.repl.completer;
+package org.apache.ignite.internal.cli.core.repl.completer.filter;
 
 import static org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION;
 import static org.apache.ignite.internal.cli.commands.Options.Constants.HELP_OPTION;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ExclusionsCompleterFilter.java
similarity index 95%
copy from modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilter.java
copy to modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ExclusionsCompleterFilter.java
index 30d4d2c601..f2d0165678 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilter.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ExclusionsCompleterFilter.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.core.repl.completer;
+package org.apache.ignite.internal.cli.core.repl.completer.filter;
 
 import java.util.Arrays;
 import java.util.Set;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/NonRepeatableOptionsFilter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/NonRepeatableOptionsFilter.java
new file mode 100644
index 0000000000..a5011cbf97
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/NonRepeatableOptionsFilter.java
@@ -0,0 +1,79 @@
+/*
+ * 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.core.repl.completer.filter;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Model.ITypeInfo;
+
+/** Filters out non-repeatable options from candidates. */
+public class NonRepeatableOptionsFilter implements CompleterFilter {
+
+    private final CommandSpec topCommandSpec;
+
+    public NonRepeatableOptionsFilter(CommandSpec spec) {
+        this.topCommandSpec = spec;
+    }
+
+    /** Filters candidates. */
+    @Override
+    public String[] filter(String[] words, String[] candidates) {
+        CommandSpec commandSpec = findCommandSpec(words);
+        Map<String, ITypeInfo> optionTypes = commandSpec.options().stream()
+                .flatMap(it -> Arrays.stream(it.names()).map(name -> new OptionInfo(name, it.typeInfo())))
+                .collect(Collectors.toMap(OptionInfo::getName, OptionInfo::getType));
+        Set<String> shouldBeExcludedFromCandidates = Arrays.stream(words)
+                .filter(optionTypes::containsKey)
+                .filter(it -> !optionTypes.get(it).isMultiValue())
+                .collect(Collectors.toSet());
+        return Arrays.stream(candidates)
+                .filter(it -> !shouldBeExcludedFromCandidates.contains(it))
+                .toArray(String[]::new);
+    }
+
+    private CommandSpec findCommandSpec(String[] words) {
+        int cursor = 0;
+        CommandSpec commandSpec = topCommandSpec;
+        while (commandSpec.subcommands().containsKey(words[cursor])) {
+            commandSpec = commandSpec.subcommands().get(words[cursor]).getCommandSpec();
+            cursor++;
+        }
+        return commandSpec;
+    }
+
+    private static class OptionInfo {
+        private final String name;
+        private final ITypeInfo type;
+
+        private OptionInfo(String name, ITypeInfo type) {
+            this.name = name;
+            this.type = type;
+        }
+
+        private String getName() {
+            return name;
+        }
+
+        private ITypeInfo getType() {
+            return type;
+        }
+    }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ShortOptionsFilter.java
similarity index 61%
rename from modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilter.java
rename to modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ShortOptionsFilter.java
index 30d4d2c601..43eed57f8a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilter.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ShortOptionsFilter.java
@@ -15,25 +15,30 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.core.repl.completer;
+package org.apache.ignite.internal.cli.core.repl.completer.filter;
 
 import java.util.Arrays;
 import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.ignite.internal.cli.commands.Options;
 
-/** Filters out exclusions from candidates. */
-public class ExclusionsCompleterFilter implements CompleterFilter {
+/** Filters out short option names from candidates. */
+public class ShortOptionsFilter implements CompleterFilter {
 
-    private final Set<String> exclusions;
+    private final Set<String> shortOptionNames = shortOptionNames();
 
-    public ExclusionsCompleterFilter(String... exclusions) {
-        this.exclusions = Set.of(exclusions);
+    private static Set<String> shortOptionNames() {
+        return Arrays.stream(Options.values())
+                .filter(it -> !it.fullName().equals(it.shortName()))
+                .map(Options::shortName)
+                .collect(Collectors.toSet());
     }
 
     /** Filters candidates. */
     @Override
     public String[] filter(String[] ignored, String[] candidates) {
         return Arrays.stream(candidates)
-                .filter(it -> !exclusions.contains(it))
+                .filter(it -> !shortOptionNames.contains(it))
                 .toArray(String[]::new);
     }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java
index 75485b2c47..e7097097ec 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/IgnitePicocliCommands.java
@@ -23,9 +23,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import org.apache.ignite.internal.cli.core.repl.completer.CompleterFilter;
 import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleter;
 import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterRegistry;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.CompleterFilter;
 import org.jline.builtins.Options.HelpException;
 import org.jline.console.ArgDesc;
 import org.jline.console.CmdDesc;
@@ -191,7 +191,7 @@ public class IgnitePicocliCommands implements CommandRegistry {
             assert candidates != null;
 
             // let picocli generate completion candidates for the token where the cursor is at
-            final String[] words = commandLine.words().toArray(new String[0]);
+            String[] words = commandLine.words().toArray(new String[0]);
             List<CharSequence> cs = new ArrayList<CharSequence>();
             AutoComplete.complete(cmd.getCommandSpec(),
                     words,
@@ -199,11 +199,9 @@ public class IgnitePicocliCommands implements CommandRegistry {
                     0,
                     commandLine.cursor(),
                     cs);
-
-            List<String> staticCandidates = new ArrayList<>();
-            for (CharSequence c : cs) {
-                staticCandidates.add(c.toString());
-            }
+            String[] staticCandidates = cs.stream()
+                    .map(CharSequence::toString)
+                    .toArray(String[]::new);
 
             List<DynamicCompleter> completers = completerRegistry.findCompleters(words);
             if (!completers.isEmpty()) {
@@ -222,9 +220,9 @@ public class IgnitePicocliCommands implements CommandRegistry {
                 return;
             }
 
-            String[] filteredCandidates = completerFilters.get(0).filter(words, staticCandidates.toArray(String[]::new));
-            for (int i = 1; i < completerFilters.size(); i++) {
-                filteredCandidates = completerFilters.get(i).filter(words, filteredCandidates);
+            String[] filteredCandidates = staticCandidates;
+            for (CompleterFilter filter : completerFilters) {
+                filteredCandidates = filter.filter(words, filteredCandidates);
             }
 
             for (String c : filteredCandidates) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
index e7226aef06..ce394c2abc 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
@@ -33,8 +33,11 @@ import org.apache.ignite.internal.cli.core.flow.question.JlineQuestionWriterRead
 import org.apache.ignite.internal.cli.core.flow.question.QuestionAskerFactory;
 import org.apache.ignite.internal.cli.core.repl.Repl;
 import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterActivationPoint;
-import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterFilter;
 import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterRegistry;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.CompleterFilter;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.DynamicCompleterFilter;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.NonRepeatableOptionsFilter;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.ShortOptionsFilter;
 import org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
 import org.apache.ignite.internal.cli.core.repl.expander.NoopExpander;
 import org.jline.console.impl.SystemRegistryImpl;
@@ -175,7 +178,11 @@ public class ReplExecutor {
         activationPoint.activateDynamicCompleter(completerRegistry);
 
         DynamicCompleterFilter dynamicCompleterFilter = factory.create(DynamicCompleterFilter.class);
+        List<CompleterFilter> filters = List.of(dynamicCompleterFilter,
+                new ShortOptionsFilter(),
+                new NonRepeatableOptionsFilter(cmd.getCommandSpec())
+        );
 
-        return new IgnitePicocliCommands(cmd, completerRegistry, List.of(dynamicCompleterFilter));
+        return new IgnitePicocliCommands(cmd, completerRegistry, filters);
     }
 }
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilterTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/DynamicCompleterFilterTest.java
similarity index 96%
rename from modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilterTest.java
rename to modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/DynamicCompleterFilterTest.java
index 9d238c6d96..ee0c5df94a 100644
--- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterFilterTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/DynamicCompleterFilterTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.core.repl.completer;
+package org.apache.ignite.internal.cli.core.repl.completer.filter;
 
 import static java.util.Arrays.asList;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.emptyArray;
 
 import org.apache.ignite.internal.cli.core.repl.Session;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.DynamicCompleterFilter;
 import org.junit.jupiter.api.Test;
 
 class DynamicCompleterFilterTest {
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilterTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ExclusionsCompleterFilterTest.java
similarity index 90%
rename from modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilterTest.java
rename to modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ExclusionsCompleterFilterTest.java
index 225aea12b2..76093ecc70 100644
--- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/ExclusionsCompleterFilterTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ExclusionsCompleterFilterTest.java
@@ -15,13 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.core.repl.completer;
+package org.apache.ignite.internal.cli.core.repl.completer.filter;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.hasSize;
 
 import java.util.List;
+import org.apache.ignite.internal.cli.core.repl.completer.filter.ExclusionsCompleterFilter;
 import org.junit.jupiter.api.Test;
 
 class ExclusionsCompleterFilterTest {
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/NonRepeatableOptionsFilterTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/NonRepeatableOptionsFilterTest.java
new file mode 100644
index 0000000000..89c0b311c9
--- /dev/null
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/NonRepeatableOptionsFilterTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.core.repl.completer.filter;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasSize;
+
+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.Arrays;
+import java.util.List;
+import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine;
+
+@MicronautTest
+class NonRepeatableOptionsFilterTest {
+
+    @Inject
+    private ApplicationContext ctx;
+
+    private CommandLine cmd;
+
+    @BeforeEach
+    void setUp() {
+        cmd = new CommandLine(TopLevelCliReplCommand.class, new MicronautFactory(ctx));
+    }
+
+    @Test
+    void filterNonRepeatableOptions() {
+        NonRepeatableOptionsFilter filter = new NonRepeatableOptionsFilter(cmd.getCommandSpec());
+        String[] words = {"cluster", "init", "--cluster-name", "name", "--cmg-node", "node"};
+        String[] candidates = {"--cluster-name", "--cmg-node", "--cluster-endpoint-url", "--meta-storage-node"};
+        List<String> filteredCandidates = Arrays.asList(filter.filter(words, candidates));
+        assertThat(filteredCandidates, hasSize(3));
+        assertThat(filteredCandidates, containsInAnyOrder("--cmg-node", "--cluster-endpoint-url", "--meta-storage-node"));
+    }
+}
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ShortOptionsFilterTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ShortOptionsFilterTest.java
new file mode 100644
index 0000000000..49b9fa0230
--- /dev/null
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/filter/ShortOptionsFilterTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.core.repl.completer.filter;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.arrayWithSize;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.ignite.internal.cli.commands.Options;
+import org.junit.jupiter.api.Test;
+
+class ShortOptionsFilterTest {
+
+    @Test
+    void filterShortOptions() {
+        String[] allOptions = Arrays.stream(Options.values())
+                .flatMap(it -> Stream.of(it.fullName(), it.shortName()))
+                .distinct()
+                .toArray(String[]::new);
+        String[] candidates = new ShortOptionsFilter().filter(new String[0], allOptions);
+        List<String> longOptionNames = Arrays.stream(Options.values())
+                .map(Options::fullName)
+                .collect(Collectors.toList());
+        assertThat(candidates, arrayWithSize(longOptionNames.size()));
+        assertThat(Arrays.asList(candidates), containsInAnyOrder(longOptionNames.toArray(new String[0])));
+    }
+}