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 eab1c58007 IGNITE-16520 Refactor IgniteCliInterfaceTest (#3762) eab1c58007 is described below commit eab1c580077ed5655928a954a93e7d7cdc6c5489 Author: Vadim Pakhnushev <8614891+valep...@users.noreply.github.com> AuthorDate: Wed May 15 12:45:19 2024 +0300 IGNITE-16520 Refactor IgniteCliInterfaceTest (#3762) --- .../ignite/internal/cli/CliIntegrationTest.java | 9 +- .../cli/commands/ItClusterCommandTest.java | 220 --------- .../internal/cli/commands/ItNodeNameTest.java | 1 - .../commands/cluster/init/ItClusterInitTest.java | 15 +- .../configuration/ItConfigCommandTest.java | 4 - .../cli/commands/connect/ItConnectCommandTest.java | 1 - ...tConnectWithBasicAuthenticationCommandTest.java | 6 - .../commands/questions/ItConnectToClusterTest.java | 2 - .../cli/commands/sql/ItSqlConnectSslTest.java | 3 +- .../cli/commands/sql/ItSqlReplCommandTest.java | 6 - .../cli/commands/unit/ItDeploymentUnitTest.java | 13 - .../apache/ignite/internal/cli/ssl/ItSslTest.java | 2 - .../internal/cli/IgniteCliInterfaceTest.java | 527 --------------------- .../internal/cli/commands/CliCommandTestBase.java | 80 ++-- .../cli/commands/IgniteCliInterfaceTestBase.java | 50 ++ .../cliconfig/CliConfigGetCommandTest.java | 6 +- .../cliconfig/CliConfigProfileListCommandTest.java | 22 +- .../cliconfig/CliConfigShowCommandTest.java | 10 +- .../cli/commands/cluster/ClusterInitTest.java | 199 ++++++++ .../commands/cluster/config/ClusterConfigTest.java | 84 ++++ .../cli/commands/node/config/NodeConfigTest.java | 86 ++++ .../cli/commands/node/metric/NodeMetricTest.java | 93 ++++ .../ignite/internal/cli/AbstractCliTest.java | 74 --- 23 files changed, 590 insertions(+), 923 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 62a9ce76ae..0f657fccc9 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 @@ -45,6 +45,7 @@ import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry; import org.apache.ignite.internal.cli.event.EventPublisher; import org.apache.ignite.internal.cli.event.Events; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import picocli.CommandLine; @@ -90,6 +91,11 @@ public abstract class CliIntegrationTest extends ClusterPerClassIntegrationTest @Inject private EventListeningActivationPoint eventListeningActivationPoint; + @BeforeAll + static void setDumbTerminal() { + System.setProperty("org.jline.terminal.dumb", "true"); + } + @BeforeEach void setUp() { configManagerProvider.setConfigFile(TestConfigManagerHelper.createIntegrationTestsConfig()); @@ -105,7 +111,7 @@ public abstract class CliIntegrationTest extends ClusterPerClassIntegrationTest eventPublisher.publish(Events.disconnect()); } - protected void resetOutput() { + private void resetOutput() { sout = new StringWriter(); serr = new StringWriter(); cmd.setOut(new PrintWriter(sout)); @@ -117,6 +123,7 @@ public abstract class CliIntegrationTest extends ClusterPerClassIntegrationTest } protected void execute(String... args) { + resetOutput(); exitCode = cmd.execute(args); } diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItClusterCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItClusterCommandTest.java deleted file mode 100644 index fbdc32f4dd..0000000000 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItClusterCommandTest.java +++ /dev/null @@ -1,220 +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 java.util.concurrent.TimeUnit.SECONDS; -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; -import static org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName; -import static org.awaitility.Awaitility.await; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import org.apache.ignite.IgnitionManager; -import org.apache.ignite.internal.cli.AbstractCliTest; -import org.apache.ignite.internal.testframework.TestIgnitionManager; -import org.apache.ignite.internal.testframework.WorkDirectory; -import org.apache.ignite.internal.testframework.WorkDirectoryExtension; -import org.apache.ignite.internal.testframework.log4j2.LogInspector; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; -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 cluster} commands. - */ -@ExtendWith(WorkDirectoryExtension.class) -class ItClusterCommandTest extends AbstractCliTest { - private static final String TOPOLOGY_SNAPSHOT_LOG_RECORD_PREFIX = "Topology snapshot [nodes="; - - private static final Node FIRST_NODE = new Node(0, 10100, 10300); - - private static final Node SECOND_NODE = new Node(1, 11100, 11300); - - private static final Node THIRD_NODE = new Node(2, 12100, 12300); - - private static final Node FOURTH_NODE = new Node(3, 13100, 13300); - - private static final List<Node> NODES = List.of(FIRST_NODE, SECOND_NODE, THIRD_NODE, FOURTH_NODE); - - private static final String NL = System.lineSeparator(); - - @BeforeEach - void setup(@WorkDirectory Path workDir, TestInfo testInfo) throws Exception { - CountDownLatch allNodesAreInPhysicalTopology = new CountDownLatch(1); - - LogInspector topologyLogInspector = new LogInspector( - "org.apache.ignite.internal.network.scalecube.ScaleCubeTopologyService", - evt -> { - String msg = evt.getMessage().getFormattedMessage(); - if (msg.startsWith(TOPOLOGY_SNAPSHOT_LOG_RECORD_PREFIX)) { - var ids = msg.substring(TOPOLOGY_SNAPSHOT_LOG_RECORD_PREFIX.length(), msg.lastIndexOf(']')) - .split(","); - - return ids.length == NODES.size(); - } - return false; - }, - allNodesAreInPhysicalTopology::countDown); - - topologyLogInspector.start(); - - try { - startClusterWithoutInit(workDir, testInfo); - - waitTillAllNodesJoinPhysicalTopology(allNodesAreInPhysicalTopology); - } finally { - topologyLogInspector.stop(); - } - } - - private void startClusterWithoutInit(Path workDir, TestInfo testInfo) { - NODES.parallelStream().forEach(node -> startNodeWithoutInit(node, workDir, testInfo)); - } - - private void waitTillAllNodesJoinPhysicalTopology(CountDownLatch allNodesAreInPhysicalTopology) throws InterruptedException { - assertTrue(allNodesAreInPhysicalTopology.await(10, SECONDS), "Physical topology was not formed in time"); - } - - /** - * Initiates node start and waits till it makes its REST endpoints available, but does NOT invoke init. - * - * @param node node - * @param workDir working directory - * @param testInfo test info - */ - private void startNodeWithoutInit(Node node, Path workDir, TestInfo testInfo) { - String nodeName = testNodeName(testInfo, node.nodeIndex); - - String config; - try { - config = configJsonFor(node); - } catch (IOException e) { - throw new RuntimeException("Cannot load config", e); - } - - TestIgnitionManager.start(nodeName, config, workDir.resolve(nodeName)); - } - - private String configJsonFor(Node node) throws IOException { - String config = Files.readString(Path.of("src/integrationTest/resources/hardcoded-ports-config.json")); - config = config.replaceAll("<NETWORK_PORT>", String.valueOf(node.networkPort)); - config = config.replaceAll("<REST_PORT>", String.valueOf(node.restPort)); - config = config.replaceAll("<CLIENT_PORT>", String.valueOf(node.restPort + 7000)); - config = config.replaceAll("<NET_CLUSTER_NODES>", netClusterNodes()); - - return config; - } - - private String netClusterNodes() { - return NODES.stream() - .map(Node::networkHostPort) - .map(s -> "\"" + s + "\"") - .collect(joining(", ", "[", "]")); - } - - @AfterEach - void tearDown(TestInfo testInfo) { - for (int i = 0; i < NODES.size(); i++) { - IgnitionManager.stop(testNodeName(testInfo, i)); - } - } - - /** - * Starts a cluster of 4 nodes and executes init command on it. First node is used to issue the command via REST endpoint, - * second will host the Meta Storage, third will host the Cluster Management Group (CMG), fourth - * will be just a node. - * - * @param testInfo test info (used to derive node names) - */ - @Test - void initClusterWithNodesOfDifferentRoles(TestInfo testInfo) throws InterruptedException { - int exitCode = execute( - "cluster", "init", - "--cluster-endpoint-url", FIRST_NODE.restHostPort(), - "--meta-storage-node", SECOND_NODE.nodeName(testInfo), - "--cmg-node", THIRD_NODE.nodeName(testInfo), - "--cluster-name", "ignite-cluster" - ); - - assertThat( - String.format("Wrong exit code; std is '%s', stderr is '%s'", out.toString(UTF_8), err.toString(UTF_8)), - exitCode, is(0) - ); - - assertThat(out.toString(UTF_8), is("Cluster was initialized successfully" + NL)); - - Matcher<String> nodeNameMatcher = NODES.stream() - .map(node -> node.nodeName(testInfo)) - .map(Matchers::containsString) - .collect(collectingAndThen(toList(), (List<Matcher<? super String>> matchers) -> allOf(matchers))); - - await().untilAsserted(() -> { - out.reset(); - err.reset(); - - int code = execute( - "cluster", "topology", "logical", - "--cluster-endpoint-url", FIRST_NODE.restHostPort() - ); - - assertThat( - String.format("Wrong exit code; std is '%s', stderr is '%s'", out.toString(UTF_8), err.toString(UTF_8)), - code, is(0) - ); - assertThat(out.toString(UTF_8), nodeNameMatcher); - }); - } - - private static class Node { - private final int nodeIndex; - private final int networkPort; - private final int restPort; - - private Node(int nodeIndex, int networkPort, int restPort) { - this.nodeIndex = nodeIndex; - this.networkPort = networkPort; - this.restPort = restPort; - } - - String nodeName(TestInfo testInfo) { - return testNodeName(testInfo, nodeIndex); - } - - String networkHostPort() { - return "localhost:" + networkPort; - } - - String restHostPort() { - return "http://localhost:" + restPort; - } - } -} diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java index c4c219ac67..7838d3074f 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java @@ -40,7 +40,6 @@ public class ItNodeNameTest extends CliIntegrationTest { @BeforeEach void connect() { execute("connect"); - resetOutput(); // wait to pulling node names await().until(() -> !nodeNameRegistry.names().isEmpty()); } diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java index 6eee56dcfe..938ae3f7c6 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java @@ -47,18 +47,18 @@ public class ItClusterInitTest extends CliCommandTestNotInitializedIntegrationBa // when connect(NODE_URL); - resetOutput(); - File clusterConfigurationFile = TestConfigManagerHelper.readClusterConfigurationWithEnabledAuthFile(); execute( "cluster", "init", - "--meta-storage-node", testNodeName(TEST_INFO, 0), + "--meta-storage-node", testNodeName(TEST_INFO, 1), + "--cmg-node", testNodeName(TEST_INFO, 2), "--cluster-name", "cluster", "--cluster-config-file", clusterConfigurationFile.getAbsolutePath() ); assertAll( + this::assertExitCodeIsZero, this::assertErrOutputIsEmpty, () -> assertOutputContains("Cluster was initialized successfully") ); @@ -75,6 +75,12 @@ public class ItClusterInitTest extends CliCommandTestNotInitializedIntegrationBa // REST is available assertRestIsAvailable(); + + execute("cluster", "topology", "logical"); + assertExitCodeIsZero(); + for (int i = 0; i < initialNodes(); i++) { + assertOutputContains(testNodeName(TEST_INFO, i)); + } } private void awaitClusterInitialized() throws InterruptedException { @@ -82,7 +88,6 @@ public class ItClusterInitTest extends CliCommandTestNotInitializedIntegrationBa } private void assertRestIsUnavailable() { - resetOutput(); execute("cluster", "config", "show"); assertAll( @@ -92,7 +97,6 @@ public class ItClusterInitTest extends CliCommandTestNotInitializedIntegrationBa } private void assertRestIsAvailable() { - resetOutput(); execute("cluster", "config", "show"); assertAll( @@ -100,5 +104,4 @@ public class ItClusterInitTest extends CliCommandTestNotInitializedIntegrationBa this::assertOutputIsNotEmpty ); } - } 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 24a33ac214..ed82c0760e 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 @@ -188,8 +188,6 @@ class ItConfigCommandTest extends CliIntegrationTest { this::assertOutputIsEmpty ); - resetOutput(); - execute("cluster", "config", "update", "--cluster-endpoint-url", NODE_URL, "\"security.authentication.providers.default={type=basic,users=[{username: --verbose, password=--verbose}]}\""); @@ -210,8 +208,6 @@ class ItConfigCommandTest extends CliIntegrationTest { this::assertOutputIsEmpty ); - resetOutput(); - execute("node", "config", "update", "--node-url", NODE_URL, "network.shutdownQuietPeriod=asd"); assertAll( 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 df4120cb1b..caacb4d43a 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 @@ -106,7 +106,6 @@ class ItConnectCommandTest extends ItConnectToClusterTestBase { assertThat(getPrompt()).isEqualTo("[" + nodeName() + "]> "); // When connect again - resetOutput(); execute("connect"); // Then diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java index 0a3e23430a..8254daa849 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java @@ -310,8 +310,6 @@ class ItConnectWithBasicAuthenticationCommandTest extends ItConnectToClusterTest // And prompt shows username and node name assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> "); - resetOutput(); - // Should ask user to reconnect with different user, answer "y" bindAnswers("y"); @@ -348,8 +346,6 @@ class ItConnectWithBasicAuthenticationCommandTest extends ItConnectToClusterTest // And prompt shows username and node name assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> "); - resetOutput(); - // Should ask user to reconnect with different user, answer "y" bindAnswers("y"); @@ -386,8 +382,6 @@ class ItConnectWithBasicAuthenticationCommandTest extends ItConnectToClusterTest // And prompt shows username from parameters and node name assertThat(getPrompt()).isEqualTo("[admin1:" + nodeName() + "]> "); - resetOutput(); - // Should ask user to reconnect with different user, answer "y" bindAnswers("y"); diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java index 525595462a..fb28a2ee1d 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java @@ -105,11 +105,9 @@ class ItConnectToClusterTest extends ItConnectToClusterTestBase { bindAnswers("y"); // And disconnect - resetOutput(); execute("disconnect"); // When connect to different URL - resetOutput(); execute("connect", "http://localhost:10301"); // Then diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlConnectSslTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlConnectSslTest.java index d8e006a7e8..8485683081 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlConnectSslTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlConnectSslTest.java @@ -62,12 +62,13 @@ class ItSqlConnectSslTest extends CliSqlConnectCommandTestBase { // Given connected state execute("connect"); + assertOutputIs("Connected to " + NODE_URL + System.lineSeparator()); + // When execute("sql", "select * from person"); // Then the query is failed assertAll( - () -> assertOutputIs("Connected to " + NODE_URL + System.lineSeparator()), () -> assertErrOutputContains("Connection failed"), () -> assertErrOutputContains("Handshake error") ); diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java index 780db3d804..8901e999a1 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java @@ -54,8 +54,6 @@ class ItSqlReplCommandTest extends CliIntegrationTest { this::assertErrOutputIsEmpty ); - resetOutput(); - execute("--jdbc-url", JDBC_URL); assertAll( @@ -74,8 +72,6 @@ class ItSqlReplCommandTest extends CliIntegrationTest { this::assertErrOutputIsEmpty ); - resetOutput(); - execute("SELECT COUNT(*) FROM MULTILINE_TABLE;", "--jdbc-url", JDBC_URL); assertAll( @@ -93,8 +89,6 @@ class ItSqlReplCommandTest extends CliIntegrationTest { () -> assertErrOutputContains("nonexisting] not found") ); - resetOutput(); - execute("--jdbc-url", JDBC_URL); assertAll( diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java index f7aa377dfb..967a14484b 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java @@ -132,7 +132,6 @@ public class ItDeploymentUnitTest extends CliIntegrationTest { ); await().untilAsserted(() -> { - resetOutput(); execute("cluster", "unit", "list", "--plain", id); assertDeployed(id); @@ -169,25 +168,21 @@ public class ItDeploymentUnitTest extends CliIntegrationTest { ); await().untilAsserted(() -> { - resetOutput(); execute("cluster", "unit", "list", "--plain", id); // Unit is deployed on all requested nodes assertDeployed(id); - resetOutput(); execute("node", "unit", "list", "--plain", "--node-url", "http://localhost:10300", id); // Unit is deployed on the CMG node assertDeployed(id); - resetOutput(); execute("node", "unit", "list", "--plain", "--node-url", "http://localhost:10301", id); // Unit is deployed on the requested node assertDeployed(id); - resetOutput(); execute("node", "unit", "list", "--plain", "--node-url", "http://localhost:10302", id); // Unit is not deployed on the other node @@ -214,15 +209,12 @@ public class ItDeploymentUnitTest extends CliIntegrationTest { ); await().untilAsserted(() -> { - resetOutput(); execute("cluster", "unit", "list", "--plain", id); // Unit is deployed on all requested nodes assertDeployed(id); CLUSTER.runningNodes().forEach(ignite -> { - resetOutput(); - String nodeUrl = "http://" + ignite.restHttpAddress().toString(); execute("node", "unit", "list", "--plain", "--node-url", nodeUrl, id); @@ -246,7 +238,6 @@ public class ItDeploymentUnitTest extends CliIntegrationTest { ); // When deploy second unit with version - resetOutput(); execute("cluster", "unit", "deploy", "test-unit2", "--version", "2.1", "--path", testFile2); // Then @@ -257,13 +248,11 @@ public class ItDeploymentUnitTest extends CliIntegrationTest { ); await().untilAsserted(() -> { - resetOutput(); execute("cluster", "unit", "list", "--plain", "test-unit"); assertDeployed("test-unit"); }); - resetOutput(); execute("node", "unit", "list", "--plain", "test-unit"); assertDeployed("test-unit"); @@ -275,7 +264,6 @@ public class ItDeploymentUnitTest extends CliIntegrationTest { execute("cluster", "unit", "deploy", "test-unit", "--version", "1.0.0", "--path", testFile); await().untilAsserted(() -> { - resetOutput(); execute("cluster", "unit", "list", "--plain", "test-unit"); assertDeployed("test-unit"); @@ -284,7 +272,6 @@ public class ItDeploymentUnitTest extends CliIntegrationTest { execute("cluster", "unit", "deploy", "test-unit", "--version", "2.0.0", "--path", testFile); await().untilAsserted(() -> { - resetOutput(); execute("cluster", "unit", "list", "--plain", "test-unit"); assertDeployed(List.of(new UnitIdVersion("test-unit", "1.0.0"), new UnitIdVersion("test-unit", "*2.0.0"))); diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/ItSslTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/ItSslTest.java index 457c242944..d900ad4d3f 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/ItSslTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/ItSslTest.java @@ -67,7 +67,6 @@ public class ItSslTest extends CliSslNotInitializedIntegrationTestBase { setConfigProperty(CliConfigKeys.REST_TRUST_STORE_PASSWORD, NodeConfig.trustStorePassword); setConfigProperty(CliConfigKeys.REST_KEY_STORE_PATH, NodeConfig.resolvedKeystorePath); setConfigProperty(CliConfigKeys.REST_KEY_STORE_PASSWORD, "wrong-password"); - resetOutput(); // And connect via HTTPS connect("https://localhost:10401"); @@ -125,7 +124,6 @@ public class ItSslTest extends CliSslNotInitializedIntegrationTestBase { execute("cli", "config", "set", "ignite.rest.key-store.password=" + NodeConfig.keyStorePassword); execute("cli", "config", "set", "ignite.rest.trust-store.path=" + NodeConfig.resolvedTruststorePath + "-wrong-path"); execute("cli", "config", "set", "ignite.rest.trust-store.password=" + NodeConfig.keyStorePassword); - resetOutput(); // And connect via HTTPS connect("https://localhost:10401"); diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java deleted file mode 100644 index 7ce754c99b..0000000000 --- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java +++ /dev/null @@ -1,527 +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; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.stream.Collectors.toList; -import static org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper.copyResourceToTempFile; -import static org.apache.ignite.internal.cli.core.style.AnsiStringSupport.fg; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.HttpStatusCode.INTERNAL_SERVER_ERROR_500; -import static org.mockserver.model.HttpStatusCode.OK_200; -import static org.mockserver.model.JsonBody.json; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import org.apache.ignite.internal.cli.core.style.AnsiStringSupport.Color; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockserver.integration.ClientAndServer; -import org.mockserver.junit.jupiter.MockServerExtension; -import org.mockserver.model.MediaType; - -/** - * Smoke test for Ignite CLI features and its UI. Structure of tests should be self-documented and repeat the structure of Ignite CLI - * subcommands. - */ -@DisplayName("ignite") -@ExtendWith(MockitoExtension.class) -@ExtendWith(MockServerExtension.class) -public class IgniteCliInterfaceTest extends AbstractCliTest { - private final ClientAndServer clientAndServer; - - private final String mockUrl; - - public IgniteCliInterfaceTest(ClientAndServer clientAndServer) { - this.clientAndServer = clientAndServer; - mockUrl = "http://localhost:" + clientAndServer.getPort(); - } - - /** - * Sets up environment before test execution. - */ - @BeforeEach - void setup() { - resetStreams(); - - clientAndServer.reset(); - } - - private int execute(String cmdLine) { - return execute(cmdLine.split(" ")); - } - - /** - * Tests "node" command. - */ - @Nested - @DisplayName("node") - class Node { - - /** - * Tests "config" command. - */ - @Nested - @DisplayName("config") - class Config { - @Test - @DisplayName("show --node-url http://localhost:10300") - void show() { - clientAndServer - .when(request() - .withMethod("GET") - .withPath("/management/v1/configuration/node") - ) - .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); - - int exitCode = execute("node config show --node-url " + mockUrl); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("{\n" - + " \"autoAdjust\" : {\n" - + " \"enabled\" : true\n" - + " }\n" - + "}\n" - ); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("show --node-url http://localhost:10300 local.baseline") - void showSubtree() { - clientAndServer - .when(request() - .withMethod("GET") - .withPath("/management/v1/configuration/node/local.baseline") - ) - .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); - - int exitCode = execute("node config show --node-url " + mockUrl + " local.baseline"); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("{\n" - + " \"autoAdjust\" : {\n" - + " \"enabled\" : true\n" - + " }\n" - + "}\n" - ); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("update --node-url http://localhost:10300 local.baseline.autoAdjust.enabled=true") - void updateHocon() { - clientAndServer - .when(request() - .withMethod("PATCH") - .withPath("/management/v1/configuration/node") - .withBody("local.baseline.autoAdjust.enabled=true") - ) - .respond(response(null)); - - int exitCode = execute("node config update --node-url " + mockUrl + " local.baseline.autoAdjust.enabled=true"); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Node configuration updated. " - + fg(Color.YELLOW).mark("Restart the node to apply changes.")); - assertThatStderrIsEmpty(); - } - } - - @Nested - @DisplayName("metric") - class Metric { - @Test - @DisplayName("metric source enable srcName") - void enable() { - clientAndServer - .when(request() - .withMethod("POST") - .withPath("/management/v1/metric/node/enable") - .withBody("srcName") - ) - .respond(response(null)); - - int exitCode = execute("node metric source enable --node-url " + mockUrl + " srcName"); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Metric source was enabled successfully"); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("metric source disable srcName") - void disable() { - clientAndServer - .when(request() - .withMethod("POST") - .withPath("/management/v1/metric/node/disable") - .withBody("srcName") - ) - .respond(response(null)); - - int exitCode = execute("node metric source disable --node-url " + mockUrl + " srcName"); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Metric source was disabled successfully"); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("metric source list") - void listSources() { - String responseBody = "[{\"name\":\"enabledMetric\",\"enabled\":true},{\"name\":\"disabledMetric\",\"enabled\":false}]"; - clientAndServer - .when(request() - .withMethod("GET") - .withPath("/management/v1/metric/node/source") - ) - .respond(response(responseBody)); - - int exitCode = execute("node metric source list --plain --node-url " + mockUrl); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Set name\tEnabled\nenabledMetric\tenabled\ndisabledMetric\tdisabled\n"); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("metric list") - void listSets() { - String responseBody = "[{\"name\":\"metricSet\",\"metrics\":[{\"name\":\"metric\",\"desc\":\"description\"}]}]"; - clientAndServer - .when(request() - .withMethod("GET") - .withPath("/management/v1/metric/node/set") - ) - .respond(response(responseBody)); - - int exitCode = execute("node metric list --plain --node-url " + mockUrl); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Set name\tMetric name\tDescription\nmetricSet\t\t\n\tmetric\tdescription"); - assertThatStderrIsEmpty(); - } - } - } - - /** - * Tests "cluster" command. - */ - @Nested - @DisplayName("cluster") - class Cluster { - @Test - @DisplayName("init --cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId --meta-storage-node node2ConsistentId " - + "--cmg-node node2ConsistentId --cmg-node node3ConsistentId --cluster-name cluster") - void initSuccess() { - var expectedSentContent = "{\"metaStorageNodes\":[\"node1ConsistentId\",\"node2ConsistentId\"]," - + "\"cmgNodes\":[\"node2ConsistentId\",\"node3ConsistentId\"]," - + "\"clusterName\":\"cluster\"}"; - - clientAndServer - .when(request() - .withMethod("POST") - .withPath("/management/v1/cluster/init") - .withBody(json(expectedSentContent, ONLY_MATCHING_FIELDS)) - .withContentType(MediaType.APPLICATION_JSON_UTF_8) - ) - .respond(response(null)); - - - int exitCode = execute( - "cluster", "init", - "--cluster-endpoint-url", mockUrl, - "--meta-storage-node", "node1ConsistentId", - "--meta-storage-node", "node2ConsistentId", - "--cmg-node", "node2ConsistentId", - "--cmg-node", "node3ConsistentId", - "--cluster-name", "cluster" - ); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Cluster was initialized successfully"); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName( - "init --cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId --meta-storage-node node2ConsistentId " - + "--cmg-node node2ConsistentId --cmg-node node3ConsistentId --cluster-name cluster " - + "--auth-enabled --basic-auth-username admin --basic-auth-password password") - void initWithAuthenticationSuccess() throws IOException { - - Path clusterConfigurationFile = copyResourceToTempFile("cluster-configuration-with-enabled-auth.conf").toPath(); - String clusterConfiguration = Files.readString(clusterConfigurationFile); - - var expectedSentContent = "{\n" - + " \"metaStorageNodes\": [\n" - + " \"node1ConsistentId\",\n" - + " \"node2ConsistentId\"\n" - + " ],\n" - + " \"cmgNodes\": [\n" - + " \"node2ConsistentId\",\n" - + " \"node3ConsistentId\"\n" - + " ],\n" - + " \"clusterName\": \"cluster\",\n" - + " \"clusterConfiguration\": \"" + clusterConfiguration + "\"\n" - + "}"; - - clientAndServer - .when(request() - .withMethod("POST") - .withPath("/management/v1/cluster/init") - .withBody(json(expectedSentContent, ONLY_MATCHING_FIELDS)) - .withContentType(MediaType.APPLICATION_JSON_UTF_8) - ) - .respond(response(null)); - - int exitCode = execute( - "cluster", "init", - "--cluster-endpoint-url", mockUrl, - "--meta-storage-node", "node1ConsistentId", - "--meta-storage-node", "node2ConsistentId", - "--cmg-node", "node2ConsistentId", - "--cmg-node", "node3ConsistentId", - "--cluster-name", "cluster", - "--cluster-config-file", clusterConfigurationFile.toString() - ); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Cluster was initialized successfully"); - assertThatStderrIsEmpty(); - } - - @Test - void initError() { - clientAndServer - .when(request() - .withMethod("POST") - .withPath("/management/v1/cluster/init") - ) - .respond(response() - .withStatusCode(INTERNAL_SERVER_ERROR_500.code()) - .withBody("{\"status\":500, \"detail\":\"Oops\"}") - ); - - int exitCode = execute( - "cluster", "init", - "--cluster-endpoint-url", mockUrl, - "--meta-storage-node", "node1ConsistentId", - "--meta-storage-node", "node2ConsistentId", - "--cmg-node", "node2ConsistentId", - "--cmg-node", "node3ConsistentId", - "--cluster-name", "cluster" - ); - - assertThatExitCodeIs(1, exitCode); - - assertThatStdoutIsEmpty(); - assertErrOutputEqual("Oops"); - } - - @Test - @DisplayName("init --cluster-endpoint-url http://localhost:10300 --cmg-node node2ConsistentId --cmg-node node3ConsistentId") - void metastorageNodesAreMandatoryForInit() { - int exitCode = execute( - "cluster", "init", - "--cluster-endpoint-url", mockUrl, - "--cmg-node", "node2ConsistentId", - "--cmg-node", "node3ConsistentId", - "--cluster-name", "cluster" - ); - - assertThatExitCodeIs(2, exitCode); - - assertThatStdoutIsEmpty(); - assertThat(err.toString(UTF_8), startsWith("Missing required option: '--meta-storage-node=<metaStorageNodes>'")); - } - - @Test - @DisplayName("init --cluster-endpoint-url http://localhost:10300 --meta-storage-node node2ConsistentId --meta-storage-node node3ConsistentId") - void cmgNodesAreNotMandatoryForInit() { - clientAndServer - .when(request() - .withMethod("POST") - .withPath("/management/v1/cluster/init") - ) - .respond(response().withStatusCode(OK_200.code())); - - int exitCode = execute( - "cluster", "init", - "--cluster-endpoint-url", mockUrl, - "--meta-storage-node", "node1ConsistentId", - "--meta-storage-node", "node2ConsistentId", - "--cluster-name", "cluster" - ); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Cluster was initialized successfully"); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("init --cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId --cmg-node node2ConsistentId") - void clusterNameIsMandatoryForInit() { - int exitCode = execute( - "cluster", "init", - "--cluster-endpoint-url", mockUrl, - "--meta-storage-node", "node1ConsistentId", - "--cmg-node", "node2ConsistentId" - ); - - assertThatExitCodeIs(2, exitCode); - - assertThatStdoutIsEmpty(); - assertThat(err.toString(UTF_8), startsWith("Missing required option: '--cluster-name=<clusterName>'")); - } - - @Nested - @DisplayName("config") - class Config { - @Test - @DisplayName("show --cluster-endpoint-url http://localhost:10300") - void show() { - clientAndServer - .when(request() - .withMethod("GET") - .withPath("/management/v1/configuration/cluster") - ) - .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); - - int exitCode = execute("cluster config show --cluster-endpoint-url " + mockUrl); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("{\n" - + " \"autoAdjust\" : {\n" - + " \"enabled\" : true\n" - + " }\n" - + "}\n"); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("show --cluster-endpoint-url http://localhost:10300 local.baseline") - void showSubtree() { - clientAndServer - .when(request() - .withMethod("GET") - .withPath("/management/v1/configuration/cluster/local.baseline") - ) - .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); - - int exitCode = execute("cluster config show --cluster-endpoint-url " + mockUrl + " local.baseline"); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("{\n" - + " \"autoAdjust\" : {\n" - + " \"enabled\" : true\n" - + " }\n" - + "}\n"); - assertThatStderrIsEmpty(); - } - - @Test - @DisplayName("update --cluster-endpoint-url http://localhost:10300 local.baseline.autoAdjust.enabled=true") - void updateHocon() { - clientAndServer - .when(request() - .withMethod("PATCH") - .withPath("/management/v1/configuration/cluster") - .withBody("local.baseline.autoAdjust.enabled=true") - ) - .respond(response(null)); - - int exitCode = execute("cluster config update --cluster-endpoint-url " - + mockUrl + " local.baseline.autoAdjust.enabled=true"); - - assertThatExitCodeMeansSuccess(exitCode); - - assertOutputEqual("Cluster configuration was updated successfully"); - assertThatStderrIsEmpty(); - } - } - } - - private void assertThatStdoutIsEmpty() { - assertThat(out.toString(UTF_8), is("")); - } - - private void assertThatStderrIsEmpty() { - assertThat(err.toString(UTF_8), is("")); - } - - private void assertThatExitCodeMeansSuccess(int exitCode) { - assertThatExitCodeIs(0, exitCode); - } - - private void assertThatExitCodeIs(int expectedCode, int exitCode) { - assertEquals(expectedCode, exitCode, outputStreams()); - } - - private String outputStreams() { - return "stdout:\n" + out.toString(UTF_8) + "\n" + "stderr:\n" + err.toString(UTF_8); - } - - /** - * <em>Assert</em> that {@code expected} and {@code actual} are equals ignoring differences in line separators. - * - * <p>If both are {@code null}, they are considered equal. - * - * @param exp Expected result. - * @param actual Actual result. - * @see Object#equals(Object) - */ - private static void assertEqualsIgnoreLineSeparators(String exp, String actual) { - assertEquals( - exp.lines().collect(toList()), - actual.lines().collect(toList()) - ); - } - - private void assertOutputEqual(String exp) { - assertEqualsIgnoreLineSeparators(exp, out.toString(UTF_8)); - } - - private void assertErrOutputEqual(String exp) { - assertEqualsIgnoreLineSeparators(exp, err.toString(UTF_8)); - } -} 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 19994ab7a6..4ecbb12070 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 @@ -17,7 +17,15 @@ package org.apache.ignite.internal.cli.commands; -import static org.assertj.core.api.Assertions.assertThat; +import static java.util.stream.Collectors.toList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -30,10 +38,14 @@ import jakarta.inject.Inject; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; +import java.util.List; 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.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeAll; import org.mockito.ArgumentCaptor; import picocli.CommandLine; @@ -51,6 +63,11 @@ public abstract class CliCommandTestBase extends BaseIgniteAbstractTest { private int exitCode = Integer.MIN_VALUE; + @BeforeAll + static void setDumbTerminal() { + System.setProperty("org.jline.terminal.dumb", "true"); + } + protected abstract Class<?> getCommandClass(); protected void execute(String argsLine) { @@ -70,9 +87,7 @@ public abstract class CliCommandTestBase extends BaseIgniteAbstractTest { } protected void assertExitCodeIs(int expectedExitCode) { - assertThat(exitCode) - .as("Expected exit code to be: " + expectedExitCode + " but was " + exitCode) - .isEqualTo(expectedExitCode); + assertThat("Unexpected exit code", exitCode, is(expectedExitCode)); } protected void assertExitCodeIsZero() { @@ -80,51 +95,60 @@ public abstract class CliCommandTestBase extends BaseIgniteAbstractTest { } protected void assertOutputIsNotEmpty() { - assertThat(sout.toString()) - .as("Expected command output not to be empty") - .isNotEmpty(); + assertThat("Unexpected command output", sout.toString(), is(not(emptyString()))); } protected void assertOutputIs(String expectedOutput) { - assertThat(sout.toString()) - .as("Expected command output to be: " + expectedOutput + " but was " + sout.toString()) - .isEqualTo(expectedOutput); + assertEqualsIgnoreLineSeparators("Unexpected command output", sout.toString(), expectedOutput); } protected void assertOutputContains(String... expectedOutput) { - assertThat(sout.toString()) - .as("Expected command output to contain: " + Arrays.toString(expectedOutput) + " but was " + sout.toString()) - .contains(expectedOutput); + List<Matcher<? super String>> matchers = Arrays.stream(expectedOutput).map(Matchers::containsString).collect(toList()); + assertThat("Unexpected command output", sout.toString(), allOf(matchers)); } protected void assertOutputIsEmpty() { - assertThat(sout.toString()) - .as("Expected command output to be empty") - .isEmpty(); + assertThat("Unexpected command output", sout.toString(), is(emptyString())); } protected void assertErrOutputIsNotEmpty() { - assertThat(serr.toString()) - .as("Expected command error output not to be empty") - .isNotEmpty(); + assertThat("Unexpected command error output", serr.toString(), is(not(emptyString()))); } protected void assertErrOutputIsEmpty() { - assertThat(serr.toString()) - .as("Expected command error output to be empty") - .isEmpty(); + assertThat("Unexpected command error output", serr.toString(), is(emptyString())); } protected void assertErrOutputIs(String expectedErrOutput) { - assertThat(serr.toString()) - .as("Expected command error output to be equal to: " + expectedErrOutput) - .isEqualTo(expectedErrOutput); + assertEqualsIgnoreLineSeparators("Unexpected command error output", serr.toString(), expectedErrOutput); } protected void assertErrOutputContains(String expectedErrOutput) { - assertThat(serr.toString()) - .as("Expected command error output to contain: " + expectedErrOutput + " but was " + serr.toString()) - .contains(expectedErrOutput); + assertThat("Unexpected command error output", serr.toString(), containsString(expectedErrOutput)); + } + + /** + * Asserts that the command's exit code is zero, output is equal to the expected output and the error output is empty. + * + * @param expectedOutput Expected command output. + */ + protected void assertSuccessfulOutputIs(String expectedOutput) { + assertAll( + this::assertExitCodeIsZero, + () -> assertOutputIs(expectedOutput), + this::assertErrOutputIsEmpty + ); + } + + /** + * Asserts that {@code expected} and {@code actual} are equals ignoring differences in line separators. + * + * @param reason Description of the assertion. + * @param exp Expected result. + * @param actual Actual result. + */ + private static void assertEqualsIgnoreLineSeparators(String reason, String exp, String actual) { + assertThat(reason, exp.lines().collect(toList()), contains(actual.lines().toArray(String[]::new))); } protected <IT extends CallInput, OT, T extends Call<IT, OT>> T registerMockCall(Class<T> callClass) { diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/IgniteCliInterfaceTestBase.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/IgniteCliInterfaceTestBase.java new file mode 100644 index 0000000000..157581b0e6 --- /dev/null +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/IgniteCliInterfaceTestBase.java @@ -0,0 +1,50 @@ +/* + * 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 org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.junit.jupiter.MockServerExtension; + +/** + * Base class for testing CLI interface. + */ +@ExtendWith(MockServerExtension.class) +public class IgniteCliInterfaceTestBase extends CliCommandTestBase { + protected static ClientAndServer clientAndServer; + + protected static String mockUrl; + + @Override + protected Class<?> getCommandClass() { + return TopLevelCliCommand.class; + } + + @BeforeAll + static void initMockServer(ClientAndServer clientAndServer) { + IgniteCliInterfaceTestBase.clientAndServer = clientAndServer; + mockUrl = "http://localhost:" + clientAndServer.getPort(); + } + + @BeforeEach + void resetMockServer() { + clientAndServer.reset(); + } +} diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigGetCommandTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigGetCommandTest.java index d0054f2cf6..863d4e52f1 100644 --- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigGetCommandTest.java +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigGetCommandTest.java @@ -47,11 +47,7 @@ class CliConfigGetCommandTest extends CliConfigCommandTestBase { // When executed with single key execute("server"); - assertAll( - this::assertExitCodeIsZero, - () -> assertOutputIs("127.0.0.1" + System.lineSeparator()), - this::assertErrOutputIsEmpty - ); + assertSuccessfulOutputIs("127.0.0.1" + System.lineSeparator()); } @Test diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java index 506e13da0e..d034cace59 100644 --- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java @@ -17,8 +17,6 @@ package org.apache.ignite.internal.cli.commands.cliconfig; -import static org.junit.jupiter.api.Assertions.assertAll; - import org.apache.ignite.internal.cli.commands.cliconfig.profile.CliConfigProfileListCommand; import org.junit.jupiter.api.Test; @@ -32,12 +30,8 @@ class CliConfigProfileListCommandTest extends CliConfigCommandTestBase { public void testWithDefaultProfile() { execute(); - String expectedResult = "owner" + System.lineSeparator() - + "database" + System.lineSeparator(); - assertAll( - () -> assertOutputIs(expectedResult), - this::assertErrOutputIsEmpty - ); + assertSuccessfulOutputIs("owner" + System.lineSeparator() + + "database" + System.lineSeparator()); } @Test @@ -45,11 +39,7 @@ class CliConfigProfileListCommandTest extends CliConfigCommandTestBase { configManagerProvider.setConfigFile(TestConfigManagerHelper.createOneSectionWithDefaultProfileConfig()); execute(); - String expectedResult = "default" + System.lineSeparator(); - assertAll( - () -> assertOutputIs(expectedResult), - this::assertErrOutputIsEmpty - ); + assertSuccessfulOutputIs("default" + System.lineSeparator()); } @Test @@ -57,10 +47,6 @@ class CliConfigProfileListCommandTest extends CliConfigCommandTestBase { configManagerProvider.setConfigFile(TestConfigManagerHelper.createEmptyConfig()); execute(); - String expectedResult = "default" + System.lineSeparator(); - assertAll( - () -> assertOutputIs(expectedResult), - this::assertErrOutputIsEmpty - ); + assertSuccessfulOutputIs("default" + System.lineSeparator()); } } diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java index 04f13910dd..8807e94a3c 100644 --- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java @@ -51,14 +51,8 @@ class CliConfigShowCommandTest extends CliConfigCommandTestBase { public void testWithProfile() { execute("--profile owner"); - String expectedResult = "[owner]" + System.lineSeparator() + assertSuccessfulOutputIs("[owner]" + System.lineSeparator() + "name=John Smith" + System.lineSeparator() - + "organization=Apache Ignite" + System.lineSeparator(); - - assertAll( - this::assertExitCodeIsZero, - () -> assertOutputIs(expectedResult), - this::assertErrOutputIsEmpty - ); + + "organization=Apache Ignite" + System.lineSeparator()); } } diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/ClusterInitTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/ClusterInitTest.java new file mode 100644 index 0000000000..32e0139532 --- /dev/null +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/ClusterInitTest.java @@ -0,0 +1,199 @@ +/* + * 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; + +import static org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper.copyResourceToTempFile; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; +import static org.mockserver.model.HttpStatusCode.INTERNAL_SERVER_ERROR_500; +import static org.mockserver.model.HttpStatusCode.OK_200; +import static org.mockserver.model.JsonBody.json; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.apache.ignite.internal.cli.commands.IgniteCliInterfaceTestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockserver.model.MediaType; + +/** Tests "cluster init" command. */ +@DisplayName("cluster init") +class ClusterInitTest extends IgniteCliInterfaceTestBase { + @Test + @DisplayName("--cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId" + + " --meta-storage-node node2ConsistentId --cmg-node node2ConsistentId --cmg-node node3ConsistentId --cluster-name cluster") + void initSuccess() { + var expectedSentContent = "{\"metaStorageNodes\":[\"node1ConsistentId\",\"node2ConsistentId\"]," + + "\"cmgNodes\":[\"node2ConsistentId\",\"node3ConsistentId\"]," + + "\"clusterName\":\"cluster\"}"; + + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/cluster/init") + .withBody(json(expectedSentContent, ONLY_MATCHING_FIELDS)) + .withContentType(MediaType.APPLICATION_JSON_UTF_8) + ) + .respond(response(null)); + + execute( + "cluster", "init", + "--cluster-endpoint-url", mockUrl, + "--meta-storage-node", "node1ConsistentId", + "--meta-storage-node", "node2ConsistentId", + "--cmg-node", "node2ConsistentId", + "--cmg-node", "node3ConsistentId", + "--cluster-name", "cluster" + ); + + assertSuccessfulOutputIs("Cluster was initialized successfully"); + } + + @Test + @DisplayName("--cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId --meta-storage-node node2ConsistentId" + + " --cmg-node node2ConsistentId --cmg-node node3ConsistentId --cluster-name cluster" + + " --auth-enabled --basic-auth-username admin --basic-auth-password password") + void initWithAuthenticationSuccess() throws IOException { + + Path clusterConfigurationFile = copyResourceToTempFile("cluster-configuration-with-enabled-auth.conf").toPath(); + String clusterConfiguration = Files.readString(clusterConfigurationFile); + + var expectedSentContent = "{\n" + + " \"metaStorageNodes\": [\n" + + " \"node1ConsistentId\",\n" + + " \"node2ConsistentId\"\n" + + " ],\n" + + " \"cmgNodes\": [\n" + + " \"node2ConsistentId\",\n" + + " \"node3ConsistentId\"\n" + + " ],\n" + + " \"clusterName\": \"cluster\",\n" + + " \"clusterConfiguration\": \"" + clusterConfiguration + "\"\n" + + "}"; + + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/cluster/init") + .withBody(json(expectedSentContent, ONLY_MATCHING_FIELDS)) + .withContentType(MediaType.APPLICATION_JSON_UTF_8) + ) + .respond(response(null)); + + execute( + "cluster", "init", + "--cluster-endpoint-url", mockUrl, + "--meta-storage-node", "node1ConsistentId", + "--meta-storage-node", "node2ConsistentId", + "--cmg-node", "node2ConsistentId", + "--cmg-node", "node3ConsistentId", + "--cluster-name", "cluster", + "--cluster-config-file", clusterConfigurationFile.toString() + ); + + assertSuccessfulOutputIs("Cluster was initialized successfully"); + } + + @Test + void initError() { + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/cluster/init") + ) + .respond(response() + .withStatusCode(INTERNAL_SERVER_ERROR_500.code()) + .withBody("{\"status\":500, \"detail\":\"Oops\"}") + ); + + execute( + "cluster", "init", + "--cluster-endpoint-url", mockUrl, + "--meta-storage-node", "node1ConsistentId", + "--meta-storage-node", "node2ConsistentId", + "--cmg-node", "node2ConsistentId", + "--cmg-node", "node3ConsistentId", + "--cluster-name", "cluster" + ); + + assertAll( + () -> assertExitCodeIs(1), + this::assertOutputIsEmpty, + () -> assertErrOutputIs("Oops") + ); + } + + @Test + @DisplayName("--cluster-endpoint-url http://localhost:10300 --cmg-node node2ConsistentId --cmg-node node3ConsistentId") + void metastorageNodesAreMandatoryForInit() { + execute( + "cluster", "init", + "--cluster-endpoint-url", mockUrl, + "--cmg-node", "node2ConsistentId", + "--cmg-node", "node3ConsistentId", + "--cluster-name", "cluster" + ); + + assertAll( + () -> assertExitCodeIs(2), + this::assertOutputIsEmpty, + () -> assertErrOutputContains("Missing required option: '--meta-storage-node=<metaStorageNodes>'") + ); + } + + @Test + @DisplayName("--cluster-endpoint-url http://localhost:10300 --meta-storage-node node2ConsistentId --meta-storage-node node3ConsistentId") + void cmgNodesAreNotMandatoryForInit() { + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/cluster/init") + ) + .respond(response().withStatusCode(OK_200.code())); + + execute( + "cluster", "init", + "--cluster-endpoint-url", mockUrl, + "--meta-storage-node", "node1ConsistentId", + "--meta-storage-node", "node2ConsistentId", + "--cluster-name", "cluster" + ); + + assertSuccessfulOutputIs("Cluster was initialized successfully"); + } + + @Test + @DisplayName("--cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId --cmg-node node2ConsistentId") + void clusterNameIsMandatoryForInit() { + execute( + "cluster", "init", + "--cluster-endpoint-url", mockUrl, + "--meta-storage-node", "node1ConsistentId", + "--cmg-node", "node2ConsistentId" + ); + + assertAll( + () -> assertExitCodeIs(2), + this::assertOutputIsEmpty, + () -> assertErrOutputContains("Missing required option: '--cluster-name=<clusterName>'") + ); + } +} 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 new file mode 100644 index 0000000000..5d6283d8ed --- /dev/null +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cluster/config/ClusterConfigTest.java @@ -0,0 +1,84 @@ +/* + * 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.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import org.apache.ignite.internal.cli.commands.IgniteCliInterfaceTestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** Tests "cluster config" commands. */ +@DisplayName("cluster config") +class ClusterConfigTest extends IgniteCliInterfaceTestBase { + @Test + @DisplayName("show --cluster-endpoint-url http://localhost:10300") + void show() { + clientAndServer + .when(request() + .withMethod("GET") + .withPath("/management/v1/configuration/cluster") + ) + .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); + + execute("cluster config show --cluster-endpoint-url " + mockUrl); + + assertSuccessfulOutputIs("{\n" + + " \"autoAdjust\" : {\n" + + " \"enabled\" : true\n" + + " }\n" + + "}\n"); + } + + @Test + @DisplayName("show --cluster-endpoint-url http://localhost:10300 local.baseline") + void showSubtree() { + clientAndServer + .when(request() + .withMethod("GET") + .withPath("/management/v1/configuration/cluster/local.baseline") + ) + .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); + + execute("cluster config show --cluster-endpoint-url " + mockUrl + " local.baseline"); + + assertSuccessfulOutputIs("{\n" + + " \"autoAdjust\" : {\n" + + " \"enabled\" : true\n" + + " }\n" + + "}\n"); + } + + @Test + @DisplayName("update --cluster-endpoint-url http://localhost:10300 local.baseline.autoAdjust.enabled=true") + void updateHocon() { + clientAndServer + .when(request() + .withMethod("PATCH") + .withPath("/management/v1/configuration/cluster") + .withBody("local.baseline.autoAdjust.enabled=true") + ) + .respond(response(null)); + + execute("cluster config update --cluster-endpoint-url " + + mockUrl + " local.baseline.autoAdjust.enabled=true"); + + assertSuccessfulOutputIs("Cluster configuration was updated successfully"); + } +} 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 new file mode 100644 index 0000000000..f25e0984cd --- /dev/null +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/config/NodeConfigTest.java @@ -0,0 +1,86 @@ +/* + * 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.node.config; + +import static org.apache.ignite.internal.cli.core.style.AnsiStringSupport.fg; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import org.apache.ignite.internal.cli.commands.IgniteCliInterfaceTestBase; +import org.apache.ignite.internal.cli.core.style.AnsiStringSupport.Color; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** Tests "node config" commands. */ +@DisplayName("node config") +public class NodeConfigTest extends IgniteCliInterfaceTestBase { + @Test + @DisplayName("show --node-url http://localhost:10300") + void show() { + clientAndServer + .when(request() + .withMethod("GET") + .withPath("/management/v1/configuration/node") + ) + .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); + + execute("node config show --node-url " + mockUrl); + + assertSuccessfulOutputIs("{\n" + + " \"autoAdjust\" : {\n" + + " \"enabled\" : true\n" + + " }\n" + + "}\n"); + } + + @Test + @DisplayName("show --node-url http://localhost:10300 local.baseline") + void showSubtree() { + clientAndServer + .when(request() + .withMethod("GET") + .withPath("/management/v1/configuration/node/local.baseline") + ) + .respond(response("{\"autoAdjust\":{\"enabled\":true}}")); + + execute("node config show --node-url " + mockUrl + " local.baseline"); + + assertSuccessfulOutputIs("{\n" + + " \"autoAdjust\" : {\n" + + " \"enabled\" : true\n" + + " }\n" + + "}\n"); + } + + @Test + @DisplayName("update --node-url http://localhost:10300 local.baseline.autoAdjust.enabled=true") + void updateHocon() { + clientAndServer + .when(request() + .withMethod("PATCH") + .withPath("/management/v1/configuration/node") + .withBody("local.baseline.autoAdjust.enabled=true") + ) + .respond(response(null)); + + execute("node config update --node-url " + mockUrl + " local.baseline.autoAdjust.enabled=true"); + + assertSuccessfulOutputIs("Node configuration updated. " + + fg(Color.YELLOW).mark("Restart the node to apply changes.")); + } +} diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/metric/NodeMetricTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/metric/NodeMetricTest.java new file mode 100644 index 0000000000..65fe0ba0b6 --- /dev/null +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/node/metric/NodeMetricTest.java @@ -0,0 +1,93 @@ +/* + * 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.node.metric; + +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import org.apache.ignite.internal.cli.commands.IgniteCliInterfaceTestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** Tests "node metric" commands. */ +@DisplayName("node metric") +class NodeMetricTest extends IgniteCliInterfaceTestBase { + @Test + @DisplayName("source enable srcName") + void enable() { + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/metric/node/enable") + .withBody("srcName") + ) + .respond(response(null)); + + execute("node metric source enable --node-url " + mockUrl + " srcName"); + + assertSuccessfulOutputIs("Metric source was enabled successfully"); + } + + @Test + @DisplayName("source disable srcName") + void disable() { + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/metric/node/disable") + .withBody("srcName") + ) + .respond(response(null)); + + execute("node metric source disable --node-url " + mockUrl + " srcName"); + + assertSuccessfulOutputIs("Metric source was disabled successfully"); + } + + @Test + @DisplayName("source list") + void listSources() { + String responseBody = "[{\"name\":\"enabledMetric\",\"enabled\":true},{\"name\":\"disabledMetric\",\"enabled\":false}]"; + clientAndServer + .when(request() + .withMethod("GET") + .withPath("/management/v1/metric/node/source") + ) + .respond(response(responseBody)); + + execute("node metric source list --plain --node-url " + mockUrl); + + assertSuccessfulOutputIs("Set name\tEnabled\nenabledMetric\tenabled\ndisabledMetric\tdisabled\n"); + } + + @Test + @DisplayName("list") + void listSets() { + String responseBody = "[{\"name\":\"metricSet\",\"metrics\":[{\"name\":\"metric\",\"desc\":\"description\"}]}]"; + clientAndServer + .when(request() + .withMethod("GET") + .withPath("/management/v1/metric/node/set") + ) + .respond(response(responseBody)); + + execute("node metric list --plain --node-url " + mockUrl); + + assertSuccessfulOutputIs("Set name\tMetric name\tDescription\nmetricSet\t\t\n\tmetric\tdescription"); + } +} diff --git a/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/AbstractCliTest.java b/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/AbstractCliTest.java deleted file mode 100644 index 983a241acb..0000000000 --- a/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/AbstractCliTest.java +++ /dev/null @@ -1,74 +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; - -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.io.ByteArrayOutputStream; -import java.io.PrintWriter; -import org.apache.ignite.internal.cli.commands.TopLevelCliCommand; -import org.junit.jupiter.api.BeforeAll; -import picocli.CommandLine; - -/** - * Base class for any CLI tests. - */ -@MicronautTest(rebuildContext = true) -public abstract class AbstractCliTest { - @Inject - private ApplicationContext ctx; - - /** stderr. */ - protected ByteArrayOutputStream err = new ByteArrayOutputStream(); - - /** stdout. */ - protected ByteArrayOutputStream out = new ByteArrayOutputStream(); - - /** - * Creates a new command line interpreter. - * - * @return New command line instance. - */ - private CommandLine cmd() { - return new CommandLine(TopLevelCliCommand.class, new MicronautFactory(ctx)) - .setErr(new PrintWriter(err, true)) - .setOut(new PrintWriter(out, true)); - } - - protected final int execute(String... args) { - return cmd().execute(args); - } - - /** - * Sets up a dumb terminal before tests. - */ - @BeforeAll - static void beforeAll() { - System.setProperty("org.jline.terminal.dumb", "true"); - } - - /** - * Reset stderr and stdout streams. - */ - protected void resetStreams() { - err.reset(); - out.reset(); - } -}