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 04bf67462a IGNITE-23267 Trailing slash in the node URL results in an 
error (#4553)
04bf67462a is described below

commit 04bf67462a949da5334f86e5332e001118773c91
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Wed Oct 16 16:24:01 2024 +0300

    IGNITE-23267 Trailing slash in the node URL results in an error (#4553)
---
 .../cli/commands/connect/ItConnectCommandTest.java | 14 ++++
 .../cli/commands/cluster/ClusterUrlMixin.java      |  4 +-
 .../cli/commands/connect/ConnectCommand.java       |  4 +-
 .../cli/commands/connect/ConnectReplCommand.java   |  4 +-
 .../internal/cli/commands/node/NodeUrlMixin.java   |  4 +-
 .../cli/commands/node/NodeUrlProfileMixin.java     |  4 +-
 .../cluster/migrate/MigrateToClusterMixin.java     |  6 +-
 ...onverter.java => RestEndpointUrlConverter.java} |  7 +-
 .../cluster/config/ClusterConfigReplTest.java}     | 21 ++---
 .../commands/cluster/config/ClusterConfigTest.java | 17 ++++
 .../commands/node/config/NodeConfigReplTest.java}  | 21 ++---
 .../cli/commands/node/config/NodeConfigTest.java   | 17 ++++
 .../migrate/MigrateToClusterCommandReplTest.java}  | 25 +++---
 .../migrate/MigrateToClusterCommandTest.java       | 95 ++++++++++++++++++++++
 .../restart/RestartPartitionsTest.java             |  3 +-
 15 files changed, 190 insertions(+), 56 deletions(-)

diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
index f7d9bc210c..fc2b243501 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
@@ -69,6 +69,20 @@ class ItConnectCommandTest extends 
ItConnectToClusterTestBase {
         );
     }
 
+    @Test
+    @DisplayName("Should connect to cluster with given url with trailing 
slash")
+    void connectWithGivenUrlWithTrailingSlash() {
+        // When connect with given url with trailing slash
+        execute("connect", "http://localhost:10301/";);
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10301";),
+                this::assertTerminalOutputIsEmpty
+        );
+    }
+
     @Test
     @DisplayName("Should not connect to cluster with wrong url")
     void connectWithWrongUrl() {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
index 39ea5fdd50..0125dc6487 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlMixin.java
@@ -22,7 +22,7 @@ import static 
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_
 import static 
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION_DESC;
 
 import java.net.URL;
-import org.apache.ignite.internal.cli.core.converters.UrlConverter;
+import org.apache.ignite.internal.cli.core.converters.RestEndpointUrlConverter;
 import picocli.CommandLine.Option;
 
 /**
@@ -33,7 +33,7 @@ public class ClusterUrlMixin {
     @Option(
             names = CLUSTER_URL_OPTION,
             description = CLUSTER_URL_OPTION_DESC,
-            converter = UrlConverter.class,
+            converter = RestEndpointUrlConverter.class,
             order = CLUSTER_URL_OPTION_ORDER
     )
     private URL clusterUrl;
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
index 28f407855a..0cc8e01fad 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommand.java
@@ -28,7 +28,7 @@ import 
org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
 import org.apache.ignite.internal.cli.call.connect.ConnectWizardCall;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
 import org.apache.ignite.internal.cli.core.call.CallExecutionPipeline;
-import org.apache.ignite.internal.cli.core.converters.UrlConverter;
+import org.apache.ignite.internal.cli.core.converters.RestEndpointUrlConverter;
 import picocli.CommandLine.ArgGroup;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Parameters;
@@ -40,7 +40,7 @@ import picocli.CommandLine.Parameters;
 public class ConnectCommand extends BaseCommand implements Callable<Integer> {
 
     /** Node URL option. */
-    @Parameters(description = NODE_URL_OPTION_DESC, descriptionKey = 
CLUSTER_URL_KEY, converter = UrlConverter.class)
+    @Parameters(description = NODE_URL_OPTION_DESC, descriptionKey = 
CLUSTER_URL_KEY, converter = RestEndpointUrlConverter.class)
     private URL nodeUrl;
 
     @ArgGroup(exclusive = false)
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
index 840acc64c9..d997826a5b 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
@@ -26,7 +26,7 @@ import 
org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
 import org.apache.ignite.internal.cli.call.connect.ConnectWizardCall;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
 import 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
-import org.apache.ignite.internal.cli.core.converters.UrlConverter;
+import org.apache.ignite.internal.cli.core.converters.RestEndpointUrlConverter;
 import org.apache.ignite.internal.cli.core.flow.builder.Flows;
 import picocli.CommandLine.ArgGroup;
 import picocli.CommandLine.Command;
@@ -39,7 +39,7 @@ import picocli.CommandLine.Parameters;
 public class ConnectReplCommand extends BaseCommand implements Runnable {
 
     /** Node URL option. */
-    @Parameters(description = NODE_URL_OPTION_DESC, descriptionKey = 
CLUSTER_URL_KEY, converter = UrlConverter.class)
+    @Parameters(description = NODE_URL_OPTION_DESC, descriptionKey = 
CLUSTER_URL_KEY, converter = RestEndpointUrlConverter.class)
     private URL nodeUrl;
 
     @ArgGroup(exclusive = false)
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
index 51691bbef0..c41208a0ea 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlMixin.java
@@ -25,7 +25,7 @@ import static 
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL
 
 import jakarta.inject.Inject;
 import java.net.URL;
-import org.apache.ignite.internal.cli.core.converters.UrlConverter;
+import org.apache.ignite.internal.cli.core.converters.RestEndpointUrlConverter;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
 import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
 import org.jetbrains.annotations.Nullable;
@@ -48,7 +48,7 @@ public class NodeUrlMixin {
         /**
          * Node URL option.
          */
-        @Option(names = NODE_URL_OPTION, description = NODE_URL_OPTION_DESC, 
converter = UrlConverter.class)
+        @Option(names = NODE_URL_OPTION, description = NODE_URL_OPTION_DESC, 
converter = RestEndpointUrlConverter.class)
         private URL nodeUrl;
 
         /**
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
index 0fd54d4888..5360464c09 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
@@ -26,7 +26,7 @@ import org.apache.ignite.internal.cli.commands.ProfileMixin;
 import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.ConfigManager;
 import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
-import org.apache.ignite.internal.cli.core.converters.UrlConverter;
+import org.apache.ignite.internal.cli.core.converters.RestEndpointUrlConverter;
 import picocli.CommandLine.Mixin;
 import picocli.CommandLine.Option;
 
@@ -35,7 +35,7 @@ import picocli.CommandLine.Option;
  */
 public class NodeUrlProfileMixin {
     /** Node URL option. */
-    @Option(names = NODE_URL_OPTION, description = NODE_URL_OPTION_DESC, 
converter = UrlConverter.class)
+    @Option(names = NODE_URL_OPTION, description = NODE_URL_OPTION_DESC, 
converter = RestEndpointUrlConverter.class)
     private URL nodeUrl;
 
     /** Profile to get default values from. */
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterMixin.java
index 30de5bd17d..21a50da00f 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterMixin.java
@@ -23,7 +23,7 @@ import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY
 import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_OLD_CLUSTER_URL_OPTION_DESC;
 
 import java.net.URL;
-import org.apache.ignite.internal.cli.core.converters.UrlConverter;
+import org.apache.ignite.internal.cli.core.converters.RestEndpointUrlConverter;
 import picocli.CommandLine.Option;
 
 /** Arguments for 'recovery cluster migrate' command. */
@@ -32,7 +32,7 @@ public class MigrateToClusterMixin {
     @Option(
             names = RECOVERY_OLD_CLUSTER_URL_OPTION,
             description = RECOVERY_OLD_CLUSTER_URL_OPTION_DESC,
-            converter = UrlConverter.class,
+            converter = RestEndpointUrlConverter.class,
             required = true
     )
     private URL oldClusterUrl;
@@ -41,7 +41,7 @@ public class MigrateToClusterMixin {
     @Option(
             names = RECOVERY_NEW_CLUSTER_URL_OPTION,
             description = RECOVERY_NEW_CLUSTER_URL_OPTION_DESC,
-            converter = UrlConverter.class,
+            converter = RestEndpointUrlConverter.class,
             required = true
     )
     private URL newClusterUrl;
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/UrlConverter.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/RestEndpointUrlConverter.java
similarity index 79%
copy from 
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/UrlConverter.java
copy to 
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/RestEndpointUrlConverter.java
index 6eda860470..c9ad84c31c 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/UrlConverter.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/RestEndpointUrlConverter.java
@@ -23,12 +23,15 @@ import picocli.CommandLine;
 import picocli.CommandLine.TypeConversionException;
 
 /**
- * Converter for @{link URL}.
+ * Converter for {@link URL} used in REST clients, strips trailing slash 
assuming the path to the endpoint always starts with a slash.
  */
-public class UrlConverter implements CommandLine.ITypeConverter<URL> {
+public class RestEndpointUrlConverter implements 
CommandLine.ITypeConverter<URL> {
     @Override
     public URL convert(String value) throws Exception {
         try {
+            if (value.endsWith("/")) {
+                value = value.substring(0, value.length() - 1);
+            }
             return new URL(value);
         } catch (MalformedURLException e) {
             throw new TypeConversionException("Invalid URL '" + value + "' (" 
+ e.getMessage() + ")");
diff --git 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigReplTest.java
similarity index 66%
copy from 
modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
copy to 
modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigReplTest.java
index 214c22d401..99547a3a54 100644
--- 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigReplTest.java
@@ -15,19 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.rest;
+package org.apache.ignite.internal.cli.commands.cluster.config;
 
-import io.micronaut.http.annotation.Controller;
-import io.micronaut.http.annotation.Get;
+import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
+import org.junit.jupiter.api.DisplayName;
 
-/**
- * Test controller. Exposes a single ping endpoint.
- */
-@Controller
-public class TestController {
-    @Get("ping")
-    public String ping() {
-        return "pong";
+/** Tests "cluster config" REPL commands. */
+@DisplayName("cluster config REPL")
+class ClusterConfigReplTest extends ClusterConfigTest {
+    @Override
+    protected Class<?> getCommandClass() {
+        return TopLevelCliReplCommand.class;
     }
-
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigTest.java
index 21b9abfd96..b952e3f7ea 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigTest.java
@@ -44,6 +44,23 @@ class ClusterConfigTest extends IgniteCliInterfaceTestBase {
                 + "}\n");
     }
 
+    @Test
+    @DisplayName("show --url http://localhost:10300/";)
+    void trailingSlash() {
+        clientAndServer
+                .when(request()
+                        .withMethod("GET")
+                        .withPath("/management/v1/configuration/cluster")
+                )
+                .respond(response("{\"autoAdjust\":{\"enabled\":true}}"));
+
+        execute("cluster config show --url " + mockUrl + "/");
+
+        assertSuccessfulOutputIs("autoAdjust {\n"
+                + "    enabled=true\n"
+                + "}\n");
+    }
+
     @Test
     @DisplayName("show --url http://localhost:10300 local.baseline")
     void showSubtree() {
diff --git 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigReplTest.java
similarity index 66%
rename from 
modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
rename to 
modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigReplTest.java
index 214c22d401..3c1abc3c36 100644
--- 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/TestController.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigReplTest.java
@@ -15,19 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.rest;
+package org.apache.ignite.internal.cli.commands.node.config;
 
-import io.micronaut.http.annotation.Controller;
-import io.micronaut.http.annotation.Get;
+import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
+import org.junit.jupiter.api.DisplayName;
 
-/**
- * Test controller. Exposes a single ping endpoint.
- */
-@Controller
-public class TestController {
-    @Get("ping")
-    public String ping() {
-        return "pong";
+/** Tests "node config" REPL commands. */
+@DisplayName("node config REPL")
+class NodeConfigReplTest extends NodeConfigTest {
+    @Override
+    protected Class<?> getCommandClass() {
+        return TopLevelCliReplCommand.class;
     }
-
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigTest.java
index 0ff8246b67..e6960862f3 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigTest.java
@@ -44,6 +44,23 @@ public class NodeConfigTest extends 
IgniteCliInterfaceTestBase {
                 + "}\n");
     }
 
+    @Test
+    @DisplayName("show --url http://localhost:10300/";)
+    void trailingSlash() {
+        clientAndServer
+                .when(request()
+                        .withMethod("GET")
+                        .withPath("/management/v1/configuration/node")
+                )
+                .respond(response("{\"autoAdjust\":{\"enabled\":true}}"));
+
+        execute("node config show --url " + mockUrl + "/");
+
+        assertSuccessfulOutputIs("autoAdjust {\n"
+                + "    enabled=true\n"
+                + "}\n");
+    }
+
     @Test
     @DisplayName("show --url http://localhost:10300 local.baseline")
     void showSubtree() {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/UrlConverter.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterCommandReplTest.java
similarity index 59%
rename from 
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/UrlConverter.java
rename to 
modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterCommandReplTest.java
index 6eda860470..25806141c4 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/UrlConverter.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterCommandReplTest.java
@@ -15,23 +15,18 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.core.converters;
+package org.apache.ignite.internal.cli.commands.recovery.cluster.migrate;
 
-import java.net.MalformedURLException;
-import java.net.URL;
-import picocli.CommandLine;
-import picocli.CommandLine.TypeConversionException;
+import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.DisplayName;
 
-/**
- * Converter for @{link URL}.
- */
-public class UrlConverter implements CommandLine.ITypeConverter<URL> {
+/** Tests "recovery cluster migrate" REPL command. */
+@DisplayName("recovery cluster migrate REPL")
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-23296";)
+class MigrateToClusterCommandReplTest extends MigrateToClusterCommandTest {
     @Override
-    public URL convert(String value) throws Exception {
-        try {
-            return new URL(value);
-        } catch (MalformedURLException e) {
-            throw new TypeConversionException("Invalid URL '" + value + "' (" 
+ e.getMessage() + ")");
-        }
+    protected Class<?> getCommandClass() {
+        return TopLevelCliReplCommand.class;
     }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterCommandTest.java
new file mode 100644
index 0000000000..8d045f165b
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/migrate/MigrateToClusterCommandTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.recovery.cluster.migrate;
+
+import static org.mockserver.model.HttpRequest.request;
+import static org.mockserver.model.HttpResponse.response;
+import static org.mockserver.model.JsonBody.json;
+
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.cli.commands.IgniteCliInterfaceTestBase;
+import org.apache.ignite.rest.client.model.ClusterState;
+import org.apache.ignite.rest.client.model.ClusterStateClusterTag;
+import org.apache.ignite.rest.client.model.MigrateRequest;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockserver.model.MediaType;
+
+/** Tests "recovery cluster migrate" commands. */
+@DisplayName("recovery cluster migrate")
+class MigrateToClusterCommandTest extends IgniteCliInterfaceTestBase {
+    @Test
+    @DisplayName("--old-cluster-url http://localhost:10300 --new-cluster-url 
http://localhost:10300";)
+    void migrate() {
+        respondWithClusterState();
+        expectMigrateRequest();
+
+        execute("recovery cluster migrate --old-cluster-url " + mockUrl + " 
--new-cluster-url " + mockUrl);
+
+        assertSuccessfulOutputIs("Successfully initiated migration.");
+    }
+
+    @Test
+    @DisplayName("--old-cluster-url http://localhost:10300/ --new-cluster-url 
http://localhost:10300/";)
+    void trailingSlash() {
+        respondWithClusterState();
+        expectMigrateRequest();
+
+        execute("recovery cluster migrate --old-cluster-url " + mockUrl + "/ 
--new-cluster-url " + mockUrl + "/");
+
+        assertSuccessfulOutputIs("Successfully initiated migration.");
+    }
+
+    private static void respondWithClusterState() {
+        ClusterState clusterState = new ClusterState()
+                .cmgNodes(List.of("node"))
+                .msNodes(List.of("node"))
+                .igniteVersion("version")
+                .clusterTag(
+                        new ClusterStateClusterTag()
+                                .clusterName("clusterName")
+                                
.clusterId(UUID.fromString("e0655494-eb02-4757-89db-ae562a170280"))
+                );
+
+        clientAndServer
+                .when(request()
+                        .withMethod("GET")
+                        .withPath("/management/v1/cluster/state")
+                )
+                .respond(response(clusterState.toJson()));
+    }
+
+    private static void expectMigrateRequest() {
+        MigrateRequest migrateRequest = new MigrateRequest()
+                .cmgNodes(List.of("node"))
+                .metaStorageNodes(List.of("node"))
+                .version("version")
+                .clusterName("clusterName")
+                
.clusterId(UUID.fromString("e0655494-eb02-4757-89db-ae562a170280"));
+
+        clientAndServer
+                .when(request()
+                        .withMethod("POST")
+                        .withPath("/management/v1/recovery/cluster/migrate")
+                        .withBody(json(migrateRequest.toJson()))
+                        .withContentType(MediaType.APPLICATION_JSON_UTF_8)
+                )
+                .respond(response());
+    }
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/restart/RestartPartitionsTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsTest.java
similarity index 96%
rename from 
modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/restart/RestartPartitionsTest.java
rename to 
modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsTest.java
index 41e94a512c..b9436c007d 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/restart/RestartPartitionsTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.commands.recovery.restart;
+package org.apache.ignite.internal.cli.commands.recovery.partitions.restart;
 
 import static 
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION;
 import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_NODE_NAMES_OPTION;
@@ -28,7 +28,6 @@ import static org.mockserver.model.HttpResponse.response;
 import static org.mockserver.model.JsonBody.json;
 
 import org.apache.ignite.internal.cli.commands.IgniteCliInterfaceTestBase;
-import 
org.apache.ignite.internal.cli.commands.recovery.partitions.restart.RestartPartitionsCommand;
 import org.apache.ignite.internal.util.ArrayUtils;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;

Reply via email to