From 8a2900e111ec159df73b96fecb6faed4beb01630 Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 19 Jul 2022 11:23:36 +0200 Subject: [PATCH 01/64] MQTT Commands > Refactor commands using mixins --- src/main/java/com/hivemq/cli/MqttCLIMain.java | 4 +- .../hivemq/cli/commands/AbstractCommand.java | 56 --- .../cli/commands/AbstractCommonFlags.java | 147 ------- .../com/hivemq/cli/commands/CliCommand.java | 25 -- .../java/com/hivemq/cli/commands/Connect.java | 54 --- .../cli/commands/ConnectRestrictions.java | 39 -- .../java/com/hivemq/cli/commands/Context.java | 26 -- .../com/hivemq/cli/commands/Disconnect.java | 30 -- .../com/hivemq/cli/commands/MqttAction.java | 23 -- .../com/hivemq/cli/commands/MqttCommand.java | 150 ------- .../java/com/hivemq/cli/commands/Publish.java | 49 --- .../com/hivemq/cli/commands/Subscribe.java | 67 --- .../com/hivemq/cli/commands/Unsubscribe.java | 28 -- .../java/com/hivemq/cli/commands/Will.java | 50 --- .../commands/cli/AbstractConnectFlags.java | 77 ---- .../cli/commands/cli/PublishCommand.java | 216 ++-------- .../cli/commands/cli/SubscribeCommand.java | 184 ++------- .../cli/commands/cli/TestBrokerCommand.java | 9 +- .../options/AuthenticationOptions.java | 14 +- .../cli/commands/options/ConnectOptions.java | 245 +++++++++++ .../ConnectRestrictionOptions.java} | 120 +++--- .../cli/commands/options/DebugOptions.java | 41 ++ .../commands/options/DisconnectOptions.java | 96 +++++ .../options/MessagePayloadOptions.java | 4 +- .../cli/commands/options/PublishOptions.java | 154 +++++++ .../cli/commands/options/SslOptions.java | 16 +- .../commands/options/SubscribeOptions.java | 145 +++++++ .../commands/options/UnsubscribeOptions.java | 52 +++ .../WillOptions.java} | 140 +++---- .../commands/shell/ClearScreenCommand.java | 17 +- .../shell/ContextDisconnectCommand.java | 94 ++--- .../commands/shell/ContextExitCommand.java | 6 +- .../commands/shell/ContextPublishCommand.java | 172 +------- .../shell/ContextSubscribeCommand.java | 160 ++------ .../commands/shell/ContextSwitchCommand.java | 33 +- .../shell/ContextUnsubscribeCommand.java | 63 +-- .../commands/shell/ListClientsCommand.java | 21 +- .../commands/shell/ShellConnectCommand.java | 113 +----- .../commands/shell/ShellContextCommand.java | 32 +- .../shell/ShellDisconnectCommand.java | 111 +---- .../cli/commands/shell/ShellExitCommand.java | 16 +- .../cli/commands/shell/VersionCommand.java | 6 +- .../cli/mqtt/AbstractMqttClientExecutor.java | 380 +++++++++--------- .../java/com/hivemq/cli/mqtt/ClientKey.java | 33 ++ .../hivemq/cli/mqtt/MqttClientExecutor.java | 92 ++--- .../mqtt/SubscribeMqtt3PublishCallback.java | 14 +- .../mqtt/SubscribeMqtt5PublishCallback.java | 14 +- .../java/com/hivemq/cli/utils/MqttUtils.java | 2 +- .../mqtt/AbstractMqttClientExecutorTest.java | 121 +++--- 49 files changed, 1449 insertions(+), 2312 deletions(-) delete mode 100644 src/main/java/com/hivemq/cli/commands/AbstractCommand.java delete mode 100644 src/main/java/com/hivemq/cli/commands/AbstractCommonFlags.java delete mode 100644 src/main/java/com/hivemq/cli/commands/CliCommand.java delete mode 100644 src/main/java/com/hivemq/cli/commands/Connect.java delete mode 100644 src/main/java/com/hivemq/cli/commands/ConnectRestrictions.java delete mode 100644 src/main/java/com/hivemq/cli/commands/Context.java delete mode 100644 src/main/java/com/hivemq/cli/commands/Disconnect.java delete mode 100644 src/main/java/com/hivemq/cli/commands/MqttAction.java delete mode 100644 src/main/java/com/hivemq/cli/commands/MqttCommand.java delete mode 100644 src/main/java/com/hivemq/cli/commands/Publish.java delete mode 100644 src/main/java/com/hivemq/cli/commands/Subscribe.java delete mode 100644 src/main/java/com/hivemq/cli/commands/Unsubscribe.java delete mode 100644 src/main/java/com/hivemq/cli/commands/Will.java delete mode 100644 src/main/java/com/hivemq/cli/commands/cli/AbstractConnectFlags.java create mode 100644 src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java rename src/main/java/com/hivemq/cli/commands/{AbstractConnectRestrictionFlags.java => options/ConnectRestrictionOptions.java} (80%) create mode 100644 src/main/java/com/hivemq/cli/commands/options/DebugOptions.java create mode 100644 src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java create mode 100644 src/main/java/com/hivemq/cli/commands/options/PublishOptions.java create mode 100644 src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java create mode 100644 src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java rename src/main/java/com/hivemq/cli/commands/{AbstractWillFlags.java => options/WillOptions.java} (70%) create mode 100644 src/main/java/com/hivemq/cli/mqtt/ClientKey.java diff --git a/src/main/java/com/hivemq/cli/MqttCLIMain.java b/src/main/java/com/hivemq/cli/MqttCLIMain.java index 5452c89b8..74c5859b1 100644 --- a/src/main/java/com/hivemq/cli/MqttCLIMain.java +++ b/src/main/java/com/hivemq/cli/MqttCLIMain.java @@ -49,7 +49,7 @@ public static void main(final @NotNull String... args) { defaultCLIProperties.init(); } catch (final Exception e) { System.err.println(e.getMessage()); - System.exit(-1); + System.exit(1); } if (args.length == 0) { @@ -93,7 +93,7 @@ public void run() { public static class CLIVersionProvider implements CommandLine.IVersionProvider { @Override - public @NotNull String @NotNull [] getVersion() throws Exception { + public @NotNull String @NotNull [] getVersion() { String version = getClass().getPackage().getImplementationVersion(); if (version == null) { version = "DEVELOPMENT"; diff --git a/src/main/java/com/hivemq/cli/commands/AbstractCommand.java b/src/main/java/com/hivemq/cli/commands/AbstractCommand.java deleted file mode 100644 index a0b8e6f52..000000000 --- a/src/main/java/com/hivemq/cli/commands/AbstractCommand.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import picocli.CommandLine; - -@CommandLine.Command(sortOptions = false, synopsisHeading = "%n@|bold Usage|@: ", - synopsisSubcommandLabel = "{ pub | sub | shell }", descriptionHeading = "%n", - optionListHeading = "%n@|bold Options|@:%n", commandListHeading = "%n@|boldCommands|@:%n", separator = " ") -public abstract class AbstractCommand implements CliCommand { - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-d", "--debug"}, defaultValue = "false", description = "Enable debug mode", order = 0) - private void activateDebugMode(final boolean debug) { - if (debug && !verbose) { - this.debug = true; - } - } - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-v", "--verbose"}, defaultValue = "false", description = "Enable verbose mode", - order = 0) - private void activateVerboseMode(final boolean verbose) { - if (verbose) { - this.verbose = true; - debug = true; - } else { - this.verbose = false; - } - } - - private boolean debug; - private boolean verbose; - - public boolean isDebug() { - return debug; - } - - public boolean isVerbose() { - return verbose; - } -} diff --git a/src/main/java/com/hivemq/cli/commands/AbstractCommonFlags.java b/src/main/java/com/hivemq/cli/commands/AbstractCommonFlags.java deleted file mode 100644 index dfc00fdee..000000000 --- a/src/main/java/com/hivemq/cli/commands/AbstractCommonFlags.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.google.common.base.Throwables; -import com.hivemq.cli.DefaultCLIProperties; -import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.SslOptions; -import com.hivemq.cli.converters.ByteBufferConverter; -import com.hivemq.cli.converters.EnvVarToByteBufferConverter; -import com.hivemq.cli.converters.PasswordFileToByteBufferConverter; -import com.hivemq.cli.converters.UnsignedShortConverter; -import com.hivemq.client.mqtt.MqttClientSslConfig; -import com.hivemq.client.mqtt.MqttWebSocketConfig; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.tinylog.Logger; -import picocli.CommandLine; - -import java.nio.ByteBuffer; -import java.util.Objects; - -public abstract class AbstractCommonFlags extends AbstractConnectRestrictionFlags implements Connect { - - @CommandLine.Option(names = {"-u", "--user"}, description = "The username for authentication", order = 2) - private @Nullable String user; - - @CommandLine.Option(names = {"-pw", "--password"}, arity = "0..1", interactive = true, - converter = ByteBufferConverter.class, description = "The password for authentication", order = 2) - private @Nullable ByteBuffer password; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-pw:env"}, arity = "0..1", converter = EnvVarToByteBufferConverter.class, - fallbackValue = "MQTT_CLI_PW", - description = "The password for authentication read in from an environment variable", order = 2) - private void setPasswordFromEnv(final @NotNull ByteBuffer passwordEnvironmentVariable) { - password = passwordEnvironmentVariable; - } - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-pw:file"}, converter = PasswordFileToByteBufferConverter.class, - description = "The password for authentication read in from a file", order = 2) - private void setPasswordFromFile(final @NotNull ByteBuffer passwordFromFile) {password = passwordFromFile;} - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-k", "--keepAlive"}, converter = UnsignedShortConverter.class, - description = "A keep alive of the client (in seconds) (default: 60)", order = 2) - private @Nullable Integer keepAlive; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-c", "--cleanStart"}, negatable = true, defaultValue = "true", - description = "Define a clean start for the connection (default: true)", order = 2) - private boolean cleanStart; - - @CommandLine.Mixin - private final @NotNull SslOptions sslOptions = new SslOptions(); - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-ws"}, description = "Use WebSocket transport protocol (default: false)", order = 2) - private boolean useWebSocket; - - @CommandLine.Option(names = {"-ws:path"}, description = "The path of the WebSocket", order = 2) - private @Nullable String webSocketPath; - - @Override - public void setDefaultOptions() { - super.setDefaultOptions(); - final DefaultCLIProperties defaultCLIProperties = - Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); - - if (user == null) { - user = defaultCLIProperties.getUsername(); - } - - if (password == null) { - try { - password = defaultCLIProperties.getPassword(); - } catch (final Exception e) { - Logger.error(e, "Default password could not be loaded ({})", Throwables.getRootCause(e).getMessage()); - } - } - - if (useWebSocket && webSocketPath == null) { - webSocketPath = defaultCLIProperties.getWebsocketPath(); - } - } - - public @Nullable MqttClientSslConfig buildSslConfig() throws Exception { - return sslOptions.buildSslConfig(); - } - - @Override - public @NotNull String toString() { - return "Connect{" + "key=" + getKey() + ", " + commonOptions() + '}'; - } - - public @NotNull String commonOptions() { - return super.toString() + (user != null ? (", user=" + user) : "") + - (keepAlive != null ? (", keepAlive=" + keepAlive) : "") + - ", cleanStart=" + cleanStart + ", sslOptions=" + sslOptions + - ", useWebSocket=" + useWebSocket + (webSocketPath != null ? (", webSocketPath=" + webSocketPath) : "") + - getWillOptions(); - } - - public @Nullable String getUser() { - return user; - } - - public void setUser(final @Nullable String user) { - this.user = user; - } - - public @Nullable ByteBuffer getPassword() { - return password; - } - - public @Nullable Integer getKeepAlive() { - return keepAlive; - } - - public @Nullable Boolean getCleanStart() { - return cleanStart; - } - - public @Nullable MqttWebSocketConfig getWebSocketConfig() { - if (useWebSocket) { - return MqttWebSocketConfig.builder().serverPath(Objects.requireNonNull(webSocketPath)).build(); - } else { - return null; - } - } - -} diff --git a/src/main/java/com/hivemq/cli/commands/CliCommand.java b/src/main/java/com/hivemq/cli/commands/CliCommand.java deleted file mode 100644 index 2e8e72158..000000000 --- a/src/main/java/com/hivemq/cli/commands/CliCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -public interface CliCommand { - - boolean isVerbose(); - - boolean isDebug(); - -} diff --git a/src/main/java/com/hivemq/cli/commands/Connect.java b/src/main/java/com/hivemq/cli/commands/Connect.java deleted file mode 100644 index 5f34598e1..000000000 --- a/src/main/java/com/hivemq/cli/commands/Connect.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.hivemq.client.mqtt.MqttClientSslConfig; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.MqttWebSocketConfig; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.nio.ByteBuffer; - -public interface Connect extends Context, Will, ConnectRestrictions { - - @NotNull String getHost(); - - int getPort(); - - @NotNull String getIdentifier(); - - @NotNull MqttVersion getVersion(); - - @Nullable String getUser(); - - @Nullable ByteBuffer getPassword(); - - @Nullable Integer getKeepAlive(); - - @Nullable Boolean getCleanStart(); - - @Nullable MqttClientSslConfig getSslConfig(); - - @Nullable Long getSessionExpiryInterval(); - - @Nullable Mqtt5UserProperties getConnectUserProperties(); - - @Nullable MqttWebSocketConfig getWebSocketConfig(); - -} diff --git a/src/main/java/com/hivemq/cli/commands/ConnectRestrictions.java b/src/main/java/com/hivemq/cli/commands/ConnectRestrictions.java deleted file mode 100644 index ab22c0c53..000000000 --- a/src/main/java/com/hivemq/cli/commands/ConnectRestrictions.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import org.jetbrains.annotations.Nullable; - -public interface ConnectRestrictions { - - @Nullable Integer getReceiveMaximum(); - - @Nullable Integer getSendMaximum(); - - @Nullable Integer getMaximumPacketSize(); - - @Nullable Integer getSendMaximumPacketSize(); - - @Nullable Integer getTopicAliasMaximum(); - - @Nullable Integer getSendTopicAliasMaximum(); - - @Nullable Boolean getRequestProblemInformation(); - - @Nullable Boolean getRequestResponseInformation(); - -} diff --git a/src/main/java/com/hivemq/cli/commands/Context.java b/src/main/java/com/hivemq/cli/commands/Context.java deleted file mode 100644 index 796aa03be..000000000 --- a/src/main/java/com/hivemq/cli/commands/Context.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import org.jetbrains.annotations.NotNull; - -public interface Context extends CliCommand { - - @NotNull String getIdentifier(); - - @NotNull String getKey(); -} diff --git a/src/main/java/com/hivemq/cli/commands/Disconnect.java b/src/main/java/com/hivemq/cli/commands/Disconnect.java deleted file mode 100644 index 45ab3332c..000000000 --- a/src/main/java/com/hivemq/cli/commands/Disconnect.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import org.jetbrains.annotations.Nullable; - -public interface Disconnect extends Context { - - @Nullable Long getSessionExpiryInterval(); - - @Nullable String getReasonString(); - - @Nullable Mqtt5UserProperties getUserProperties(); - -} diff --git a/src/main/java/com/hivemq/cli/commands/MqttAction.java b/src/main/java/com/hivemq/cli/commands/MqttAction.java deleted file mode 100644 index a5bdae8c9..000000000 --- a/src/main/java/com/hivemq/cli/commands/MqttAction.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import org.jetbrains.annotations.NotNull; - -public interface MqttAction extends Runnable { - - @NotNull String getKey(); -} diff --git a/src/main/java/com/hivemq/cli/commands/MqttCommand.java b/src/main/java/com/hivemq/cli/commands/MqttCommand.java deleted file mode 100644 index caadf4b2d..000000000 --- a/src/main/java/com/hivemq/cli/commands/MqttCommand.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.google.common.base.Joiner; -import com.google.common.primitives.Chars; -import com.hivemq.cli.DefaultCLIProperties; -import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.converters.MqttVersionConverter; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.MqttVersion; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.tinylog.Logger; -import picocli.CommandLine; - -import java.util.List; -import java.util.Objects; - -@CommandLine.Command() -public abstract class MqttCommand extends AbstractCommand implements Context { - - @CommandLine.Option(names = {"-V", "--mqttVersion"}, converter = MqttVersionConverter.class, - description = "The MQTT version used by the client (default: 5)", order = 1) - private @Nullable MqttVersion version; - - @CommandLine.Option(names = {"-h", "--host"}, - description = "The hostname of the message broker (default 'localhost')", order = 1) - private @Nullable String host; - - @CommandLine.Option(names = {"-p", "--port"}, description = "The port of the message broker (default: 1883)", - order = 1) - private @Nullable Integer port; - - @CommandLine.Option(names = {"-i", "--identifier"}, - description = "The client identifier UTF-8 String (default randomly generated string)", order = 1) - private @Nullable String identifier; - - @CommandLine.Option(names = {"-ip", "--identifierPrefix"}, - description = "The prefix of the client Identifier UTF-8 String", order = 2) - private @Nullable String identifierPrefix; - - public void setDefaultOptions() { - final DefaultCLIProperties defaultCLIProperties = - Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); - - if (version == null) { - version = defaultCLIProperties.getMqttVersion(); - Logger.trace("Setting value of 'version' to default value: {}", version); - } - if (host == null) { - host = defaultCLIProperties.getHost(); - Logger.trace("Setting value of 'host' to default value: {}", host); - } - if (port == null) { - port = defaultCLIProperties.getPort(); - Logger.trace("Setting value of 'port' to default value: {}", port); - } - if (identifierPrefix == null) { - identifierPrefix = defaultCLIProperties.getClientPrefix(); - } - if (identifier == null) { - if (version == MqttVersion.MQTT_5_0) { - identifier = ""; - Logger.trace("Empty identifier will lead to using broker generated client identifier"); - } else { - final String rndID = MqttUtils.buildRandomClientID(defaultCLIProperties.getClientLength()); - identifier = identifierPrefix + rndID; - Logger.trace("Created identifier ('{}')", identifier); - } - } - - logIdentifierWarnings(); - } - - private void logIdentifierWarnings() { - if (version == MqttVersion.MQTT_5_0 && Objects.requireNonNull(identifier).isEmpty()) { - // Client identifier will be generated by broker so no warning needs to be printed - return; - } - final List warnings = - MqttUtils.getIdentifierWarnings(Objects.requireNonNull(identifier)); - - for (final MqttUtils.IdentifierWarning warning : warnings) { - switch (warning) { - case TOO_LONG: - Logger.warn( - "Identifier '{}' may be too long (identifier length '{}' exceeds 23)", - identifier, - identifier.length()); - break; - case TOO_SHORT: - Logger.warn( - "Identifier '{}' may be too short (identifier length '{}' is less than 1)", - identifier, - identifier.length()); - break; - case CONTAINS_INVALID_CHAR: - final char[] invalidChars = MqttUtils.getInvalidIdChars(identifier); - Logger.warn( - "Identifier '{}' may contain invalid characters ({})", - identifier, - "'" + Joiner.on("', '").join(Chars.asList(invalidChars)) + "'"); - break; - } - } - } - - @Override - public @NotNull String toString() { - return "host=" + host + ", port=" + port + ", version=" + version + - (identifier != null && !identifier.isEmpty() ? (", identifier=" + identifier) : ""); - } - - @Override - public @NotNull String getKey() { - return "client {" + "identifier='" + getIdentifier() + '\'' + ", host='" + getHost() + '\'' + '}'; - } - - public @NotNull MqttVersion getVersion() { - return Objects.requireNonNull(version); - } - - public @NotNull String getHost() { - return Objects.requireNonNull(host); - } - - public int getPort() { - return Objects.requireNonNull(port); - } - - @Override - public @NotNull String getIdentifier() { - return Objects.requireNonNull(identifier); - } -} diff --git a/src/main/java/com/hivemq/cli/commands/Publish.java b/src/main/java/com/hivemq/cli/commands/Publish.java deleted file mode 100644 index 5ac417b88..000000000 --- a/src/main/java/com/hivemq/cli/commands/Publish.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.hivemq.client.mqtt.datatypes.MqttQos; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PayloadFormatIndicator; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.nio.ByteBuffer; - -public interface Publish extends Context { - - @NotNull String @NotNull [] getTopics(); - - @NotNull MqttQos @NotNull [] getQos(); - - @NotNull ByteBuffer getMessage(); - - @Nullable Boolean getRetain(); - - @Nullable Long getMessageExpiryInterval(); - - @Nullable Mqtt5PayloadFormatIndicator getPayloadFormatIndicator(); - - @Nullable String getContentType(); - - @Nullable String getResponseTopic(); - - @Nullable ByteBuffer getCorrelationData(); - - @Nullable Mqtt5UserProperties getUserProperties(); - -} diff --git a/src/main/java/com/hivemq/cli/commands/Subscribe.java b/src/main/java/com/hivemq/cli/commands/Subscribe.java deleted file mode 100644 index e7305ba42..000000000 --- a/src/main/java/com/hivemq/cli/commands/Subscribe.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.hivemq.client.mqtt.datatypes.MqttQos; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.tinylog.Logger; - -import java.io.File; -import java.io.IOException; - -public interface Subscribe extends Context { - - @NotNull String @NotNull [] getTopics(); - - @NotNull MqttQos @NotNull [] getQos(); - - @Nullable File getOutputFile(); - - boolean isPrintToSTDOUT(); - - boolean isBase64(); - - boolean isJsonOutput(); - - boolean showTopics(); - - @Nullable Mqtt5UserProperties getUserProperties(); - - default boolean outputFileInvalid(final @Nullable File outputFile) { - if (outputFile == null) { - // option --outputToFile was not used - return false; - } - if (outputFile.isDirectory()) { - Logger.error("Cannot create output file {} as it is a directory", outputFile.getAbsolutePath()); - return true; - } - - try { - if (!outputFile.createNewFile()) { // This is only false if the file already exists - Logger.debug("Writing to existing output file {}", outputFile.getAbsolutePath()); - } - } catch (final @NotNull IOException e) { - Logger.error("Could not create output file {}", outputFile.getAbsolutePath(), e); - return true; - } - - return false; - } -} diff --git a/src/main/java/com/hivemq/cli/commands/Unsubscribe.java b/src/main/java/com/hivemq/cli/commands/Unsubscribe.java deleted file mode 100644 index f76ee94f6..000000000 --- a/src/main/java/com/hivemq/cli/commands/Unsubscribe.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface Unsubscribe extends Context { - - @NotNull String @NotNull [] getTopics(); - - @Nullable Mqtt5UserProperties getUserProperties(); -} diff --git a/src/main/java/com/hivemq/cli/commands/Will.java b/src/main/java/com/hivemq/cli/commands/Will.java deleted file mode 100644 index 9e368bf44..000000000 --- a/src/main/java/com/hivemq/cli/commands/Will.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; - -import com.hivemq.client.mqtt.datatypes.MqttQos; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PayloadFormatIndicator; -import org.jetbrains.annotations.Nullable; - -import java.nio.ByteBuffer; - -public interface Will { - - @Nullable Long getWillMessageExpiryInterval(); - - @Nullable String getWillTopic(); - - @Nullable Boolean getWillRetain(); - - @Nullable Long getWillDelayInterval(); - - @Nullable Mqtt5PayloadFormatIndicator getWillPayloadFormatIndicator(); - - @Nullable String getWillContentType(); - - @Nullable String getWillResponseTopic(); - - @Nullable ByteBuffer getWillCorrelationData(); - - @Nullable Mqtt5UserProperties getWillUserProperties(); - - @Nullable ByteBuffer getWillMessage(); - - @Nullable MqttQos getWillQos(); - -} diff --git a/src/main/java/com/hivemq/cli/commands/cli/AbstractConnectFlags.java b/src/main/java/com/hivemq/cli/commands/cli/AbstractConnectFlags.java deleted file mode 100644 index 05e6ba32e..000000000 --- a/src/main/java/com/hivemq/cli/commands/cli/AbstractConnectFlags.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands.cli; - -import com.hivemq.cli.commands.AbstractCommonFlags; -import com.hivemq.cli.commands.Connect; -import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; -import com.hivemq.cli.converters.UnsignedIntConverter; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.tinylog.Logger; -import picocli.CommandLine; - -import java.util.Arrays; - -public abstract class AbstractConnectFlags extends AbstractCommonFlags implements Connect { - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-se", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the session of the connected client", order = 2) - private @Nullable Long sessionExpiryInterval; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Cup", "--connectUserProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the connect message'", order = 2) - private @Nullable Mqtt5UserProperty @Nullable [] connectUserProperties; - - @NotNull String connectOptions() { - return commonOptions() + - (sessionExpiryInterval != null ? (", sessionExpiryInterval=" + sessionExpiryInterval) : "") + - (connectUserProperties != null ? (", userProperties=" + Arrays.toString(connectUserProperties)) : "") + - connectRestrictionOptions(); - - } - - @Override - public void logUnusedOptions() { - super.logUnusedOptions(); - if (getVersion() == MqttVersion.MQTT_3_1_1) { - if (sessionExpiryInterval != null) { - Logger.warn("Connect session expiry interval was set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - - if (connectUserProperties != null) { - Logger.warn("Connect user properties were set but are unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - } - } - - public @Nullable Long getSessionExpiryInterval() { - return sessionExpiryInterval; - } - - public @Nullable Mqtt5UserProperties getConnectUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(connectUserProperties); - } -} diff --git a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java index 55766fc2f..a10cede55 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java @@ -18,33 +18,23 @@ import com.google.common.base.Throwables; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.MqttAction; -import com.hivemq.cli.commands.Publish; -import com.hivemq.cli.commands.options.MessagePayloadOptions; -import com.hivemq.cli.converters.*; +import com.hivemq.cli.commands.options.ConnectOptions; +import com.hivemq.cli.commands.options.DebugOptions; +import com.hivemq.cli.commands.options.PublishOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.MqttClientSslConfig; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.datatypes.MqttQos; +import com.hivemq.client.mqtt.MqttClient; import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; -import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PayloadFormatIndicator; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; +import java.util.concurrent.Callable; @CommandLine.Command(name = "pub", versionProvider = MqttCLIMain.CLIVersionProvider.class, aliases = "publish", description = "Publish a message to a list of topics.") -public class PublishCommand extends AbstractConnectFlags implements MqttAction, Publish { +public class PublishCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"--version"}, versionHelp = true, description = "display version info") @@ -54,69 +44,21 @@ public class PublishCommand extends AbstractConnectFlags implements MqttAction, @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") private boolean usageHelpRequested; - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required - @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to publish to", order = 1) - private @NotNull String @NotNull [] topics; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "0", - description = "Quality of service for the corresponding topic (default for all: 0)", order = 1) - private @NotNull MqttQos @NotNull [] qos; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via multiplicity = 1 - @CommandLine.ArgGroup(multiplicity = "1", order = 1) - private @NotNull MessagePayloadOptions message; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--retain"}, negatable = true, - description = "The message will be retained (default: false)", order = 1) - private boolean retain; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-e", "--messageExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the publish message in seconds (default: no message expiry)", order = 1) - private @Nullable Long messageExpiryInterval; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-pf", "--payloadFormatIndicator"}, converter = PayloadFormatIndicatorConverter.class, - description = "The payload format indicator of the publish message", order = 1) - private @Nullable Mqtt5PayloadFormatIndicator payloadFormatIndicator; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-ct", "--contentType"}, description = "A description of publish message's content", - order = 1) - private @Nullable String contentType; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-rt", "--responseTopic"}, - description = "The topic name for the publish message`s response message", order = 1) - private @Nullable String responseTopic; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-cd", "--correlationData"}, converter = ByteBufferConverter.class, - description = "The correlation data of the publish message", order = 1) - private @Nullable ByteBuffer correlationData; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the publish message", order = 1) - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", - order = 1) + description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; - private final @NotNull MqttClientExecutor mqttClientExecutor; + @CommandLine.Mixin + private final @NotNull ConnectOptions connectOptions = new ConnectOptions(); - private @Nullable MqttClientSslConfig sslConfig; + @CommandLine.Mixin + private final @NotNull PublishOptions publishOptions = new PublishOptions(); - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public PublishCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull DebugOptions debugOptions = new DebugOptions(); + + private final @NotNull MqttClientExecutor mqttClientExecutor; @Inject public PublishCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @@ -124,132 +66,42 @@ public PublishCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { } @Override - public void run() { + public Integer call() { + String logLevel = "warn"; - if (isDebug()) { + if (debugOptions.isDebug()) { logLevel = "debug"; } - if (isVerbose()) { + if (debugOptions.isVerbose()) { logLevel = "trace"; } LoggerUtils.setupConsoleLogging(logToLogfile, logLevel); - setDefaultOptions(); - try { - sslConfig = buildSslConfig(); - } catch (final Exception e) { - Logger.error(e, "Could not build SSL configuration"); - return; - } - Logger.trace("Command {} ", this); - logUnusedOptions(); + connectOptions.setDefaultOptions(); + connectOptions.logUnusedOptions(); + publishOptions.logUnusedOptions(connectOptions.getVersion()); try { - qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); - mqttClientExecutor.publish(this); + publishOptions.arrangeQosToMatchTopics(); + final MqttClient client = mqttClientExecutor.connect(connectOptions, null); + mqttClientExecutor.publish(client, publishOptions); } catch (final ConnectionFailedException cex) { - Logger.error(cex, cex.getCause().getMessage()); + Logger.error(cex, "Unable to connect: {}", cex.getCause().getMessage()); + return 1; } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + Logger.error(ex, "Unable to publish: {}", Throwables.getRootCause(ex).getMessage()); + return 1; } - } - - public void logUnusedOptions() { - super.logUnusedOptions(); - - if (getVersion() == MqttVersion.MQTT_3_1_1) { - if (messageExpiryInterval != null) { - Logger.warn("Publish message expiry was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (payloadFormatIndicator != null) { - Logger.warn("Publish payload format indicator was set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - if (contentType != null) { - Logger.warn("Publish content type was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (responseTopic != null) { - Logger.warn("Publish response topic was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (correlationData != null) { - Logger.warn("Publish correlation data was set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - if (userProperties != null) { - Logger.warn("Publish user properties were set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - } - } - - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + connectOptions() + ", topics=" + Arrays.toString(topics) + ", qos=" + - Arrays.toString(qos) + ", message=" + - new String(message.getMessageBuffer().array(), StandardCharsets.UTF_8) + - ", retain=" + retain + - (messageExpiryInterval != null ? (", messageExpiryInterval=" + messageExpiryInterval) : "") + - (payloadFormatIndicator != null ? (", payloadFormatIndicator=" + payloadFormatIndicator) : "") + - (contentType != null ? (", contentType=" + contentType) : "") + - (responseTopic != null ? (", responseTopic=" + responseTopic) : "") + (correlationData != null ? - (", correlationData=" + new String(correlationData.array(), StandardCharsets.UTF_8)) : "") + - (userProperties != null ? (", userProperties=" + getUserProperties()) : "") + '}'; - } - - @Override - public @NotNull String @NotNull [] getTopics() { - return topics; - } - - @Override - public @NotNull MqttQos @NotNull [] getQos() { - return qos; - } - - @Override - public @NotNull ByteBuffer getMessage() { - return message.getMessageBuffer(); - } - - @Override - public @Nullable Boolean getRetain() { - return retain; - } - - @Override - public @Nullable Long getMessageExpiryInterval() { - return messageExpiryInterval; - } - - @Override - public @Nullable Mqtt5PayloadFormatIndicator getPayloadFormatIndicator() { - return payloadFormatIndicator; - } - - @Override - public @Nullable String getContentType() { - return contentType; - } - @Override - public @Nullable String getResponseTopic() { - return responseTopic; - } - - @Override - public @Nullable ByteBuffer getCorrelationData() { - return correlationData; - } - - @Override - public @Nullable Mqtt5UserProperties getUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(userProperties); + return 0; } @Override - public @Nullable MqttClientSslConfig getSslConfig() { - return sslConfig; + public String toString() { + return "PublishCommand{" + "versionInfoRequested=" + versionInfoRequested + ", usageHelpRequested=" + + usageHelpRequested + ", logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + + ", publishOptions=" + publishOptions + ", debugOptions=" + debugOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java index bb748de63..e6634aad2 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java @@ -17,37 +17,30 @@ package com.hivemq.cli.commands.cli; import com.google.common.base.Throwables; -import com.hivemq.cli.DefaultCLIProperties; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.MqttAction; -import com.hivemq.cli.commands.Subscribe; -import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; -import com.hivemq.cli.converters.MqttQosConverter; +import com.hivemq.cli.commands.options.ConnectOptions; +import com.hivemq.cli.commands.options.DebugOptions; +import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; -import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.MqttClient; -import com.hivemq.client.mqtt.MqttClientSslConfig; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.io.File; -import java.util.Arrays; import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "sub", versionProvider = MqttCLIMain.CLIVersionProvider.class, aliases = "subscribe", description = "Subscribe an MQTT client to a list of topics.") -public class SubscribeCommand extends AbstractConnectFlags implements MqttAction, Subscribe { +public class SubscribeCommand implements Callable { private static final int IDLE_TIME = 5000; + private final @NotNull MqttClientExecutor mqttClientExecutor; + private @Nullable MqttClient subscribeClient; @SuppressWarnings("unused") @CommandLine.Option(names = {"--version"}, versionHelp = true, description = "display version info") @@ -57,126 +50,73 @@ public class SubscribeCommand extends AbstractConnectFlags implements MqttAction @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") private boolean usageHelpRequested; - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required - @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to subscribe to", - order = 1) - private @NotNull String @NotNull [] topics; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "2", - description = "Quality of service for the corresponding topics (default for all: 2)", order = 1) - private @NotNull MqttQos @NotNull [] qos; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the subscribe message", order = 1) - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-of", "--outputToFile"}, - description = "A file to which the received publish messages will be written", order = 1) - private @Nullable File outputFile; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-oc", "--outputToConsole"}, hidden = true, defaultValue = "true", - description = "The received messages will be written to the console (default: true)", order = 1) - private boolean printToSTDOUT; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-b64", "--base64"}, - description = "Specify the encoding of the received messages as Base64 (default: false)", order = 1) - private boolean base64; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-J", "--jsonOutput"}, defaultValue = "false", - description = "Print the received publishes in pretty JSON format", order = 1) - private boolean jsonOutput; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-T", "--showTopics"}, defaultValue = "false", - description = "Prepend the specific topic name to the received publish", order = 1) - private boolean showTopics; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", - order = 1) + description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; - private final @NotNull MqttClientExecutor mqttClientExecutor; - private final @NotNull DefaultCLIProperties defaultCLIProperties; + @CommandLine.Mixin + private final @NotNull ConnectOptions connectOptions = new ConnectOptions(); - private @Nullable MqttClient subscribeClient; - private @Nullable MqttClientSslConfig sslConfig; + @CommandLine.Mixin + private final @NotNull SubscribeOptions subscribeOptions = new SubscribeOptions(); + + @CommandLine.Mixin + private final @NotNull DebugOptions debugOptions = new DebugOptions(); @SuppressWarnings("unused") //needed for pico cli - reflection code generation public SubscribeCommand() { //noinspection ConstantConditions - this(null, null); + this(null); } @Inject - public SubscribeCommand( - final @NotNull MqttClientExecutor mqttClientExecutor, - final @NotNull DefaultCLIProperties defaultCLIProperties) { + public SubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { this.mqttClientExecutor = mqttClientExecutor; - this.defaultCLIProperties = defaultCLIProperties; } @Override - public void run() { + public Integer call() { String logLevel = "warn"; - if (isDebug()) { + if (debugOptions.isDebug()) { logLevel = "debug"; } - if (isVerbose()) { + if (debugOptions.isVerbose()) { logLevel = "trace"; } LoggerUtils.setupConsoleLogging(logToLogfile, logLevel); - setDefaultOptions(); - try { - sslConfig = buildSslConfig(); - } catch (final Exception e) { - Logger.error(e, "Could not build SSL configuration"); - return; - } - Logger.trace("Command {} ", this); - logUnusedOptions(); + connectOptions.setDefaultOptions(); + connectOptions.logUnusedOptions(); + subscribeOptions.setDefaultOptions(); + subscribeOptions.logUnusedOptions(connectOptions.getVersion()); - if (outputFileInvalid(outputFile)) { - return; + if (subscribeOptions.isOutputFileInvalid(subscribeOptions.getOutputFile())) { + return 1; } try { - qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); - subscribeClient = mqttClientExecutor.subscribe(this); + subscribeOptions.arrangeQosToMatchTopics(); + subscribeClient = mqttClientExecutor.connect(connectOptions, subscribeOptions); + mqttClientExecutor.subscribe(subscribeClient, subscribeOptions); } catch (final ConnectionFailedException cex) { - Logger.error(cex, cex.getCause().getMessage()); - return; + Logger.error(cex, "Unable to connect: {}",cex.getCause().getMessage()); + return 1; } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); - return; + Logger.error(ex, "Unable to subscribe: {}", Throwables.getRootCause(ex).getMessage()); + return 1; } try { stay(); } catch (final InterruptedException ex) { Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + return 1; } - } - @Override - public void logUnusedOptions() { - super.logUnusedOptions(); - if (getVersion() == MqttVersion.MQTT_3_1_1) { - if (userProperties != null) { - Logger.warn("Subscribe user properties were set but are unused in MQTT version {}", - MqttVersion.MQTT_3_1_1); - } - } + return 0; } private void stay() throws InterruptedException { @@ -185,58 +125,4 @@ private void stay() throws InterruptedException { } } - @Override - public void setDefaultOptions() { - super.setDefaultOptions(); - - if (outputFile == null && defaultCLIProperties.getClientSubscribeOutputFile() != null) { - Logger.trace("Setting value of 'toFile' to {}", defaultCLIProperties.getClientSubscribeOutputFile()); - outputFile = new File(defaultCLIProperties.getClientSubscribeOutputFile()); - } - - } - - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + connectOptions() + "topics=" + Arrays.toString(topics) + ", qos=" + - Arrays.toString(qos) + ", outputToConsole=" + printToSTDOUT + ", base64=" + base64 + ", jsonOutput=" + - jsonOutput + ", showTopics=" + showTopics + - (userProperties != null ? (", userProperties=" + Arrays.toString(userProperties)) : "") + - (outputFile != null ? (", publishFile=" + outputFile.getAbsolutePath()) : "") + '}'; - } - - @Override - public @NotNull String @NotNull [] getTopics() { - return topics; - } - - @Override - public @NotNull MqttQos @NotNull [] getQos() { - return qos; - } - - @Override - public @Nullable File getOutputFile() { - return outputFile; - } - - public boolean isPrintToSTDOUT() { - return printToSTDOUT; - } - - public boolean isBase64() {return base64;} - - public boolean isJsonOutput() {return jsonOutput;} - - public boolean showTopics() {return showTopics;} - - @Override - public @Nullable Mqtt5UserProperties getUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(userProperties); - } - - @Override - public @Nullable MqttClientSslConfig getSslConfig() { - return sslConfig; - } } diff --git a/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java b/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java index e97802cce..0d7ed3bc3 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java @@ -42,11 +42,12 @@ import javax.inject.Inject; import java.util.List; import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "test", description = "Tests the specified broker on different MQTT feature support and prints the results", sortOptions = false) -public class TestBrokerCommand implements Runnable { +public class TestBrokerCommand implements Callable { private static final int MAX_PAYLOAD_TEST_SIZE = 100000; // ~ 1 MB @@ -114,7 +115,7 @@ public TestBrokerCommand(final @NotNull DefaultCLIProperties defaultCLIPropertie } @Override - public void run() { + public Integer call() { LoggerUtils.turnOffConsoleLogging(logToLogfile); Logger.trace("Command {}", this); @@ -131,7 +132,7 @@ public void run() { } catch (final Exception e) { Logger.error(e, "Could not build SSL configuration"); System.err.println("Could not build SSL config - " + Throwables.getRootCause(e).getMessage()); - return; + return 1; } if (version != null) { @@ -144,6 +145,8 @@ public void run() { testMqtt3Features(); testMqtt5Features(); } + + return 0; } public void testMqtt5Features() { diff --git a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java index f677d9691..4b8fa70d6 100644 --- a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java @@ -31,32 +31,28 @@ public class AuthenticationOptions { - @CommandLine.Option(names = {"-u", "--user"}, description = "The username for authentication", order = 2) + @CommandLine.Option(names = {"-u", "--user"}, description = "The username for authentication") private @Nullable String user; @CommandLine.Option(names = {"-pw", "--password"}, arity = "0..1", interactive = true, - converter = ByteBufferConverter.class, description = "The password for authentication", order = 2) + converter = ByteBufferConverter.class, description = "The password for authentication") private @Nullable ByteBuffer password; @SuppressWarnings("unused") @CommandLine.Option(names = {"-pw:env"}, arity = "0..1", converter = EnvVarToByteBufferConverter.class, fallbackValue = "MQTT_CLI_PW", - description = "The password for authentication read in from an environment variable", order = 2) + description = "The password for authentication read in from an environment variable") private void setPasswordFromEnv(final @NotNull ByteBuffer passwordEnvironmentVariable) { password = passwordEnvironmentVariable; } @SuppressWarnings("unused") @CommandLine.Option(names = {"-pw:file"}, converter = PasswordFileToByteBufferConverter.class, - description = "The password for authentication read in from a file", order = 2) + description = "The password for authentication read in from a file") private void setPasswordFromFile(final @NotNull ByteBuffer passwordFromFile) { password = passwordFromFile; } - public AuthenticationOptions() { - setDefaultOptions(); - } - public @Nullable String getUser() {return user;} public @Nullable ByteBuffer getPassword() {return password;} @@ -66,7 +62,7 @@ public AuthenticationOptions() { return "AuthenticationOptions{" + "user='" + user + '\'' + ", password=" + password + '}'; } - private void setDefaultOptions() { + public void setDefaultOptions() { final DefaultCLIProperties properties = Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); if (user == null) { user = properties.getUsername(); diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java new file mode 100644 index 000000000..f44c5caba --- /dev/null +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java @@ -0,0 +1,245 @@ +package com.hivemq.cli.commands.options; + +import com.google.common.base.Joiner; +import com.google.common.primitives.Chars; +import com.hivemq.cli.DefaultCLIProperties; +import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; +import com.hivemq.cli.converters.MqttVersionConverter; +import com.hivemq.cli.converters.UnsignedIntConverter; +import com.hivemq.cli.converters.UnsignedShortConverter; +import com.hivemq.cli.utils.MqttUtils; +import com.hivemq.client.mqtt.MqttClientSslConfig; +import com.hivemq.client.mqtt.MqttVersion; +import com.hivemq.client.mqtt.MqttWebSocketConfig; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tinylog.Logger; +import picocli.CommandLine; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class ConnectOptions { + + @CommandLine.Option(names = {"-V", "--mqttVersion"}, converter = MqttVersionConverter.class, + description = "The MQTT version used by the client (default: 5)") + private @Nullable MqttVersion version; + + @CommandLine.Option(names = {"-h", "--host"}, + description = "The hostname of the message broker (default 'localhost')") + private @Nullable String host; + + @CommandLine.Option(names = {"-p", "--port"}, description = "The port of the message broker (default: 1883)") + private @Nullable Integer port; + + @CommandLine.Option(names = {"-i", "--identifier"}, + description = "The client identifier UTF-8 String (default randomly generated string)") + private @Nullable String identifier; + + @CommandLine.Option(names = {"-ip", "--identifierPrefix"}, + description = "The prefix of the client Identifier UTF-8 String") + private @Nullable String identifierPrefix; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-k", "--keepAlive"}, converter = UnsignedShortConverter.class, + description = "A keep alive of the client (in seconds) (default: 60)") + private @Nullable Integer keepAlive; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-c", "--cleanStart"}, negatable = true, + description = "Define a clean start for the connection (default: true)") + private @Nullable Boolean cleanStart; + + @CommandLine.Option(names = {"-se", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, + description = "The lifetime of the session of the connected client") + private @Nullable Long sessionExpiryInterval; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-Cup", "--connectUserProperty"}, converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the connect message'") + private @Nullable Mqtt5UserProperty @Nullable [] connectUserProperties; + + @CommandLine.Mixin + private final @NotNull WillOptions willOptions = new WillOptions(); + + @CommandLine.Mixin + private final @NotNull ConnectRestrictionOptions connectRestrictionOptions = new ConnectRestrictionOptions(); + + @CommandLine.Mixin + private final @NotNull AuthenticationOptions authenticationOptions = new AuthenticationOptions(); + + @CommandLine.Mixin + private final @NotNull SslOptions sslOptions = new SslOptions(); + + public @NotNull MqttVersion getVersion() { + return Objects.requireNonNull(version); + } + + public @NotNull String getHost() { + return Objects.requireNonNull(host); + } + + public int getPort() { + return Objects.requireNonNull(port); + } + + public @Nullable String getIdentifier() { + return identifier; + } + + public @Nullable Long getSessionExpiryInterval() { + return sessionExpiryInterval; + } + + public @Nullable Mqtt5UserProperties getConnectUserProperties() { + return MqttUtils.convertToMqtt5UserProperties(connectUserProperties); + } + + public @NotNull WillOptions getWillOptions() { + return willOptions; + } + + public @NotNull AuthenticationOptions getAuthenticationOptions() { + return authenticationOptions; + } + + public @NotNull ConnectRestrictionOptions getConnectRestrictionOptions() { + return connectRestrictionOptions; + } + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-ws"}, description = "Use WebSocket transport protocol (default: false)", order = 2) + private boolean useWebSocket; + + @CommandLine.Option(names = {"-ws:path"}, description = "The path of the WebSocket", order = 2) + private @Nullable String webSocketPath; + + public @Nullable MqttClientSslConfig buildSslConfig() throws Exception { + return sslOptions.buildSslConfig(); + } + + @Override + public @NotNull String toString() { + return "(host=" + host + + ", port=" + port + + ", version=" + version + + (identifier != null && !identifier.isEmpty() ? (", identifier=" + identifier) : "") + + (keepAlive != null ? (", keepAlive=" + keepAlive) : "") + + (cleanStart != null ? (", cleanStart=" + cleanStart) : "") + + (sessionExpiryInterval != null ? (", sessionExpiryInterval=" + sessionExpiryInterval) : "") + + (connectUserProperties != null ? (", userProperties=" + Arrays.toString(connectUserProperties)) : "") + + ", sslOptions=" + sslOptions + + ", useWebSocket=" + useWebSocket + + (webSocketPath != null ? (", webSocketPath=" + webSocketPath) : "") + + connectRestrictionOptions + + willOptions; + } + + public @Nullable Integer getKeepAlive() { + return keepAlive; + } + + public @Nullable Boolean getCleanStart() { + return cleanStart; + } + + public @Nullable MqttWebSocketConfig getWebSocketConfig() { + if (useWebSocket) { + return MqttWebSocketConfig.builder().serverPath(Objects.requireNonNull(webSocketPath)).build(); + } else { + return null; + } + } + + public void setDefaultOptions() { + final DefaultCLIProperties defaultCLIProperties = Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); + + if (version == null) { + version = defaultCLIProperties.getMqttVersion(); + Logger.trace("Setting value of 'version' to default value: {}", version); + } + if (host == null) { + host = defaultCLIProperties.getHost(); + Logger.trace("Setting value of 'host' to default value: {}", host); + } + if (port == null) { + port = defaultCLIProperties.getPort(); + Logger.trace("Setting value of 'port' to default value: {}", port); + } + if (identifierPrefix == null) { + identifierPrefix = defaultCLIProperties.getClientPrefix(); + } + if (identifier == null) { + if (version == MqttVersion.MQTT_5_0) { + identifier = ""; + Logger.trace("Empty identifier will lead to using broker generated client identifier"); + } else { + final String rndID = MqttUtils.buildRandomClientID(defaultCLIProperties.getClientLength()); + identifier = identifierPrefix + rndID; + Logger.trace("Created identifier ('{}')", identifier); + } + } + + if (useWebSocket && webSocketPath == null) { + webSocketPath = defaultCLIProperties.getWebsocketPath(); + } + + authenticationOptions.setDefaultOptions(); + } + + public void logUnusedOptions() { + + if (getVersion() == MqttVersion.MQTT_3_1_1) { + if (sessionExpiryInterval != null) { + Logger.warn("Connect session expiry interval was set but is unused in MQTT Version {}", + MqttVersion.MQTT_3_1_1); + } + + if (connectUserProperties != null) { + Logger.warn("Connect user properties were set but are unused in MQTT Version {}", + MqttVersion.MQTT_3_1_1); + } + } + + if (version == MqttVersion.MQTT_5_0 && Objects.requireNonNull(identifier).isEmpty()) { + // Client identifier will be generated by broker so no warning needs to be printed + return; + } + final List warnings = + MqttUtils.getIdentifierWarnings(Objects.requireNonNull(identifier)); + + for (final MqttUtils.IdentifierWarning warning : warnings) { + switch (warning) { + case TOO_LONG: + Logger.warn( + "Identifier '{}' may be too long (identifier length '{}' exceeds 23)", + identifier, + identifier.length()); + break; + case TOO_SHORT: + Logger.warn( + "Identifier '{}' may be too short (identifier length '{}' is less than 1)", + identifier, + identifier.length()); + break; + case CONTAINS_INVALID_CHAR: + final char[] invalidChars = MqttUtils.getInvalidIdChars(identifier); + Logger.warn( + "Identifier '{}' may contain invalid characters ({})", + identifier, + "'" + Joiner.on("', '").join(Chars.asList(invalidChars)) + "'"); + break; + } + } + + if (version != null) { + willOptions.logUnusedOptions(version); + connectRestrictionOptions.logUnusedOptions(version); + } + } + +} diff --git a/src/main/java/com/hivemq/cli/commands/AbstractConnectRestrictionFlags.java b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java similarity index 80% rename from src/main/java/com/hivemq/cli/commands/AbstractConnectRestrictionFlags.java rename to src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java index d77b9dbb8..029937dd4 100644 --- a/src/main/java/com/hivemq/cli/commands/AbstractConnectRestrictionFlags.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java @@ -1,20 +1,4 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; +package com.hivemq.cli.commands.options; import com.hivemq.client.mqtt.MqttVersion; import com.hivemq.client.mqtt.mqtt5.message.connect.Mqtt5ConnectRestrictions; @@ -23,61 +7,90 @@ import org.tinylog.Logger; import picocli.CommandLine; -public abstract class AbstractConnectRestrictionFlags extends AbstractWillFlags implements ConnectRestrictions { +public class ConnectRestrictionOptions { @SuppressWarnings("unused") @CommandLine.Option(names = {"--rcvMax"}, description = "The maximum amount of not acknowledged publishes with QoS 1 or 2 the client accepts from the server concurrently. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_RECEIVE_MAXIMUM + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_RECEIVE_MAXIMUM + ")") private @Nullable Integer receiveMaximum; @SuppressWarnings("unused") @CommandLine.Option(names = {"--sendMax"}, description = "The maximum amount of not acknowledged publishes with QoS 1 or 2 the client send to the server concurrently. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM + ")") private @Nullable Integer sendMaximum; @SuppressWarnings("unused") @CommandLine.Option(names = {"--maxPacketSize"}, description = "The maximum packet size the client accepts from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_MAXIMUM_PACKET_SIZE + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_MAXIMUM_PACKET_SIZE + ")") private @Nullable Integer maximumPacketSize; @SuppressWarnings("unused") @CommandLine.Option(names = {"--sendMaxPacketSize"}, description = "The maximum packet size the client sends to the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM_PACKET_SIZE + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM_PACKET_SIZE + ")") private @Nullable Integer sendMaximumPacketSize; @SuppressWarnings("unused") @CommandLine.Option(names = {"--topicAliasMax"}, description = "The maximum amount of topic aliases the client accepts from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_TOPIC_ALIAS_MAXIMUM + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_TOPIC_ALIAS_MAXIMUM + ")") private @Nullable Integer topicAliasMaximum; @SuppressWarnings("unused") @CommandLine.Option(names = {"--sendTopicAliasMax"}, description = "The maximum amount of topic aliases the client sends to the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_SEND_TOPIC_ALIAS_MAXIMUM + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_SEND_TOPIC_ALIAS_MAXIMUM + ")") private @Nullable Integer sendTopicAliasMaximum; @SuppressWarnings({"unused", "FieldMayBeFinal"}) @CommandLine.Option(names = {"--reqProblemInfo"}, negatable = true, description = "The client requests problem information from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_REQUEST_PROBLEM_INFORMATION + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_REQUEST_PROBLEM_INFORMATION + ")") private boolean requestProblemInformation = Mqtt5ConnectRestrictions.DEFAULT_REQUEST_PROBLEM_INFORMATION; @SuppressWarnings({"unused", "FieldMayBeFinal"}) @CommandLine.Option(names = {"--reqResponseInfo"}, negatable = true, description = "The client requests response information from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_REQUEST_RESPONSE_INFORMATION + ")", order = 3) + Mqtt5ConnectRestrictions.DEFAULT_REQUEST_RESPONSE_INFORMATION + ")") private boolean requestResponseInformation = Mqtt5ConnectRestrictions.DEFAULT_REQUEST_RESPONSE_INFORMATION; - @Override - public void logUnusedOptions() { - super.logUnusedOptions(); + public @Nullable Integer getReceiveMaximum() { + return receiveMaximum; + } + + public @Nullable Integer getSendMaximum() { + return sendMaximum; + } + + public @Nullable Integer getMaximumPacketSize() { + return maximumPacketSize; + } + + public @Nullable Integer getSendMaximumPacketSize() { + return sendMaximumPacketSize; + } + + public @Nullable Integer getTopicAliasMaximum() { + return topicAliasMaximum; + } + + public @Nullable Integer getSendTopicAliasMaximum() { + return sendTopicAliasMaximum; + } + + public @Nullable Boolean getRequestProblemInformation() { + return requestProblemInformation; + } + + public @Nullable Boolean getRequestResponseInformation() { + return requestResponseInformation; + } - if (getVersion() == MqttVersion.MQTT_3_1_1) { + public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { + if (mqttVersion == MqttVersion.MQTT_3_1_1) { if (receiveMaximum != null) { Logger.warn("Restriction receive maximum was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); @@ -113,55 +126,14 @@ public void logUnusedOptions() { } } - public @NotNull String connectRestrictionOptions() { + public @NotNull String toString() { return (receiveMaximum != null ? (", receiveMaximum=" + receiveMaximum) : "") + (sendMaximum != null ? (", sendMaximum=" + sendMaximum) : "") + (maximumPacketSize != null ? (", maximumPacketSize=" + maximumPacketSize) : "") + (sendMaximumPacketSize != null ? (", sendMaximumPacketSize=" + sendMaximumPacketSize) : "") + (topicAliasMaximum != null ? (", topicAliasMaximum=" + topicAliasMaximum) : "") + (sendTopicAliasMaximum != null ? (", sendTopicAliasMaximum=" + sendTopicAliasMaximum) : "") + - ", requestProblemInformation=" + requestProblemInformation + - ", requestResponseInformation=" + requestResponseInformation; - } - - @Override - public @Nullable Integer getReceiveMaximum() { - return receiveMaximum; - } - - @Override - public @Nullable Integer getSendMaximum() { - return sendMaximum; - } - - @Override - public @Nullable Integer getMaximumPacketSize() { - return maximumPacketSize; - } - - @Override - public @Nullable Integer getSendMaximumPacketSize() { - return sendMaximumPacketSize; - } - - @Override - public @Nullable Integer getTopicAliasMaximum() { - return topicAliasMaximum; - } - - @Override - public @Nullable Integer getSendTopicAliasMaximum() { - return sendTopicAliasMaximum; - } - - @Override - public @Nullable Boolean getRequestProblemInformation() { - return requestProblemInformation; - } - - @Override - public @Nullable Boolean getRequestResponseInformation() { - return requestResponseInformation; + (requestProblemInformation != null ? (", requestProblemInformation=" + requestProblemInformation) : "") + + (requestResponseInformation != null ? (", requestResponseInformation=" + requestResponseInformation) : ""); } - } diff --git a/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java b/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java new file mode 100644 index 000000000..f56209b42 --- /dev/null +++ b/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java @@ -0,0 +1,41 @@ +package com.hivemq.cli.commands.options; + +import picocli.CommandLine; + +public class DebugOptions { + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-d", "--debug"}, defaultValue = "false", description = "Enable debug mode") + private void activateDebugMode(final boolean debug) { + if (debug && !isVerbose) { + this.isDebug = true; + } + } + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-v", "--verbose"}, defaultValue = "false", description = "Enable verbose mode") + private void activateVerboseMode(final boolean verbose) { + if (verbose) { + this.isVerbose = true; + isDebug = true; + } else { + this.isVerbose = false; + } + } + + private boolean isDebug; + private boolean isVerbose; + + public boolean isDebug() { + return isDebug; + } + + public boolean isVerbose() { + return isVerbose; + } + + @Override + public String toString() { + return "DebugOptions{" + "isDebug=" + isDebug + ", isVerbose=" + isVerbose + '}'; + } +} diff --git a/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java new file mode 100644 index 000000000..b3dbda1d3 --- /dev/null +++ b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java @@ -0,0 +1,96 @@ +package com.hivemq.cli.commands.options; + +import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; +import com.hivemq.cli.converters.UnsignedIntConverter; +import com.hivemq.cli.utils.MqttUtils; +import com.hivemq.client.mqtt.MqttVersion; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tinylog.Logger; +import picocli.CommandLine; + +import java.util.Arrays; + +public class DisconnectOptions { + + @CommandLine.Option(names = {"-h", "--host"}, + description = "The hostname of the message broker (default 'localhost')") + private @Nullable String host; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-i", "--identifier"}, + description = "The client identifier UTF-8 String (default randomly generated string)") + private @Nullable String clientIdentifier; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-a", "--all"}, defaultValue = "false", + description = "Disconnect all connected clients") + private boolean disconnectAll; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-e", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, + description = "The session expiry of the disconnect (default: 0)") + private @Nullable Long sessionExpiryInterval; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-r", "--reason"}, description = "The reason of the disconnect") + private @Nullable String reasonString; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the disconnect message") + private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + + public boolean isDisconnectAll() { + return disconnectAll; + } + + public @Nullable Long getSessionExpiryInterval() { + return sessionExpiryInterval; + } + + public @Nullable String getReasonString() { + return reasonString; + } + + public @Nullable Mqtt5UserProperties getUserProperties() { + return MqttUtils.convertToMqtt5UserProperties(userProperties); + } + + public @Nullable String getHost() { + return host; + } + + public @Nullable String getClientIdentifier() { + return clientIdentifier; + } + + public void setHost(final @NotNull String host) { + this.host = host; + } + + public void logUnusedDisconnectOptions(final @NotNull MqttVersion mqttVersion) { + if (mqttVersion == MqttVersion.MQTT_3_1_1) { + if (sessionExpiryInterval != null) { + Logger.warn("Session expiry interval set but is unused in Mqtt version {}", MqttVersion.MQTT_3_1_1); + } + + if (reasonString != null) { + Logger.warn("Reason string was set but is unused in Mqtt version {}", MqttVersion.MQTT_3_1_1); + } + + if (userProperties != null) { + Logger.warn("User properties were set but are unused in Mqtt version {}", MqttVersion.MQTT_3_1_1); + } + } + } + + @Override + public String toString() { + return "DisconnectOptions{" + "host='" + host + '\'' + ", identifier='" + clientIdentifier + '\'' + + ", disconnectAll=" + disconnectAll + ", sessionExpiryInterval=" + sessionExpiryInterval + + ", reasonString='" + reasonString + '\'' + ", userProperties=" + Arrays.toString(userProperties) + '}'; + } +} diff --git a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java index 5444112ce..03f6a36d9 100644 --- a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java @@ -27,14 +27,14 @@ public class MessagePayloadOptions { @SuppressWarnings("unused") @CommandLine.Option(names = {"-m", "--message"}, converter = ByteBufferConverter.class, - description = "The message to publish", order = 1) + description = "The message to publish") private void setMessageFromCommandline(final @NotNull ByteBuffer messageFromFile) { messageBuffer = messageFromFile; } @SuppressWarnings("unused") @CommandLine.Option(names = {"-m:file", "--message-file"}, converter = FileToByteBufferConverter.class, - description = "The message read in from a file", order = 1) + description = "The message read in from a file") private void setMessageFromFile(final @NotNull ByteBuffer messageFromFile) { messageBuffer = messageFromFile; } diff --git a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java new file mode 100644 index 000000000..b7cef3a42 --- /dev/null +++ b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java @@ -0,0 +1,154 @@ +package com.hivemq.cli.commands.options; + +import com.hivemq.cli.converters.*; +import com.hivemq.cli.utils.MqttUtils; +import com.hivemq.client.mqtt.MqttVersion; +import com.hivemq.client.mqtt.datatypes.MqttQos; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; +import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PayloadFormatIndicator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tinylog.Logger; +import picocli.CommandLine; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public class PublishOptions { + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) + @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to publish to") + private @NotNull String @NotNull [] topics; + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) + @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "0", + description = "Quality of service for the corresponding topic (default for all: 0)") + private @NotNull MqttQos @NotNull [] qos; + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) + @CommandLine.ArgGroup(multiplicity = "1") + private @NotNull MessagePayloadOptions message; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-r", "--retain"}, negatable = true, + description = "The message will be retained (default: false)") + private @Nullable Boolean retain; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-e", "--messageExpiryInterval"}, converter = UnsignedIntConverter.class, + description = "The lifetime of the publish message in seconds (default: no message expiry)") + private @Nullable Long messageExpiryInterval; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-pf", "--payloadFormatIndicator"}, converter = PayloadFormatIndicatorConverter.class, + description = "The payload format indicator of the publish message") + private @Nullable Mqtt5PayloadFormatIndicator payloadFormatIndicator; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-ct", "--contentType"}, description = "A description of publish message's content", + order = 1) + private @Nullable String contentType; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-rt", "--responseTopic"}, + description = "The topic name for the publish message`s response message") + private @Nullable String responseTopic; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-cd", "--correlationData"}, converter = ByteBufferConverter.class, + description = "The correlation data of the publish message") + private @Nullable ByteBuffer correlationData; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the publish message") + private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + + public @NotNull String @NotNull [] getTopics() { + return topics; + } + + public @NotNull MqttQos @NotNull [] getQos() { + return qos; + } + + public @NotNull ByteBuffer getMessage() { + return message.getMessageBuffer(); + } + + public @Nullable Boolean getRetain() { + return retain; + } + + public @Nullable Long getMessageExpiryInterval() { + return messageExpiryInterval; + } + + public @Nullable Mqtt5PayloadFormatIndicator getPayloadFormatIndicator() { + return payloadFormatIndicator; + } + + public @Nullable String getContentType() { + return contentType; + } + + public @Nullable String getResponseTopic() { + return responseTopic; + } + + public @Nullable ByteBuffer getCorrelationData() { + return correlationData; + } + + public @Nullable Mqtt5UserProperties getUserProperties() { + return MqttUtils.convertToMqtt5UserProperties(userProperties); + } + + public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { + + if (mqttVersion == MqttVersion.MQTT_3_1_1) { + if (messageExpiryInterval != null) { + Logger.warn("Publish message expiry was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (payloadFormatIndicator != null) { + Logger.warn("Publish payload format indicator was set but is unused in MQTT Version {}", + MqttVersion.MQTT_3_1_1); + } + if (contentType != null) { + Logger.warn("Publish content type was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (responseTopic != null) { + Logger.warn("Publish response topic was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (correlationData != null) { + Logger.warn("Publish correlation data was set but is unused in MQTT Version {}", + MqttVersion.MQTT_3_1_1); + } + if (userProperties != null) { + Logger.warn("Publish user properties were set but is unused in MQTT Version {}", + MqttVersion.MQTT_3_1_1); + } + } + } + + public void arrangeQosToMatchTopics(){ + qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); + } + + public @NotNull String toString() { + return getClass().getSimpleName() + "{topics=" + Arrays.toString(topics) + + ", qos=" + + Arrays.toString(qos) + ", message=" + + new String(message.getMessageBuffer().array(), StandardCharsets.UTF_8) + + (retain != null ? (", retain=" + retain) : "") + + (messageExpiryInterval != null ? (", messageExpiryInterval=" + messageExpiryInterval) : "") + + (payloadFormatIndicator != null ? (", payloadFormatIndicator=" + payloadFormatIndicator) : "") + + (contentType != null ? (", contentType=" + contentType) : "") + + (responseTopic != null ? (", responseTopic=" + responseTopic) : "") + (correlationData != null ? + (", correlationData=" + new String(correlationData.array(), StandardCharsets.UTF_8)) : "") + + (userProperties != null ? (", userProperties=" + getUserProperties()) : "") + '}'; + } +} + diff --git a/src/main/java/com/hivemq/cli/commands/options/SslOptions.java b/src/main/java/com/hivemq/cli/commands/options/SslOptions.java index 24a039f7a..df2535741 100644 --- a/src/main/java/com/hivemq/cli/commands/options/SslOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/SslOptions.java @@ -44,37 +44,35 @@ public class SslOptions { @SuppressWarnings("unused") @CommandLine.Option(names = {"-s", "--secure"}, defaultValue = "false", - description = "Use default ssl configuration if no other ssl options are specified (default: false)", - order = 2) + description = "Use default ssl configuration if no other ssl options are specified (default: false)") private boolean useSsl; @CommandLine.Option(names = {"--cafile"}, paramLabel = "FILE", converter = FileToCertificatesConverter.class, - description = "Path to a file containing trusted CA certificates to enable encrypted certificate based communication", - order = 2) + description = "Path to a file containing trusted CA certificates to enable encrypted certificate based communication") private @Nullable Collection serverCertificateChain; @SuppressWarnings("unused") @CommandLine.Option(names = {"--capath"}, paramLabel = "DIR", converter = DirectoryToCertificatesConverter.class, description = { "Path to a directory containing certificate files to import to enable encrypted certificate based communication" - }, order = 2) + }) private @Nullable Collection serverCertificateChainFromDir; @SuppressWarnings("unused") @CommandLine.Option(names = {"--ciphers"}, split = ":", - description = "The client supported cipher suites list in IANA format separated with ':'", order = 2) + description = "The client supported cipher suites list in IANA format separated with ':'") private @Nullable Collection cipherSuites; @CommandLine.Option(names = {"--tls-version"}, - description = "The TLS protocol version to use (default: {'TLSv.1.2'})", order = 2) + description = "The TLS protocol version to use (default: {'TLSv.1.2'})") private @Nullable Collection supportedTLSVersions; @CommandLine.Option(names = {"--cert"}, converter = FileToCertificatesConverter.class, - description = "The client certificate to use for client side authentication", order = 2) + description = "The client certificate to use for client side authentication") private @Nullable Collection clientCertificateChain; @CommandLine.Option(names = {"--key"}, converter = FileToPrivateKeyConverter.class, - description = "The path to the client private key for client side authentication", order = 2) + description = "The path to the client private key for client side authentication") private @Nullable PrivateKey clientPrivateKey; private boolean useBuiltSslConfig() { diff --git a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java new file mode 100644 index 000000000..890110fd1 --- /dev/null +++ b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java @@ -0,0 +1,145 @@ +package com.hivemq.cli.commands.options; + +import com.hivemq.cli.DefaultCLIProperties; +import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; +import com.hivemq.cli.converters.MqttQosConverter; +import com.hivemq.cli.utils.MqttUtils; +import com.hivemq.client.mqtt.MqttVersion; +import com.hivemq.client.mqtt.datatypes.MqttQos; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tinylog.Logger; +import picocli.CommandLine; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +public class SubscribeOptions { + + public SubscribeOptions() { + setDefaultOptions(); + } + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required + @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to subscribe to") + private @NotNull String @NotNull [] topics; + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value + @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "2", + description = "Quality of service for the corresponding topics (default for all: 2)") + private @NotNull MqttQos @NotNull [] qos; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the subscribe message") + private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-of", "--outputToFile"}, + description = "A file to which the received publish messages will be written") + private @Nullable File outputFile; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-oc", "--outputToConsole"}, hidden = true, defaultValue = "true", + description = "The received messages will be written to the console (default: true)") + private boolean printToSTDOUT; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-b64", "--base64"}, + description = "Specify the encoding of the received messages as Base64 (default: false)") + private boolean base64; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-J", "--jsonOutput"}, defaultValue = "false", + description = "Print the received publishes in pretty JSON format") + private boolean jsonOutput; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-T", "--showTopics"}, defaultValue = "false", + description = "Prepend the specific topic name to the received publish") + private boolean showTopics; + + public @NotNull String @NotNull [] getTopics() { + return topics; + } + + public @NotNull MqttQos @NotNull [] getQos() { + return qos; + } + + public @Nullable File getOutputFile() { + return outputFile; + } + + public boolean isPrintToSTDOUT() { + return printToSTDOUT; + } + + public boolean isBase64() {return base64;} + + public boolean isJsonOutput() {return jsonOutput;} + + public @Nullable Mqtt5UserProperties getUserProperties() { + return MqttUtils.convertToMqtt5UserProperties(userProperties); + } + + public @Nullable Mqtt5UserProperty[] getUserPropertiesRaw() { + return userProperties; + } + + public boolean isShowTopics() { + return showTopics; + } + + public void setPrintToSTDOUT(final boolean printToSTDOUT) { + this.printToSTDOUT = printToSTDOUT; + } + + public boolean isOutputFileInvalid(final @Nullable File outputFile) { + if (outputFile == null) { + // option --outputToFile was not used + return false; + } + if (outputFile.isDirectory()) { + Logger.error("Cannot create output file {} as it is a directory", outputFile.getAbsolutePath()); + return true; + } + + try { + if (!outputFile.createNewFile()) { // This is only false if the file already exists + Logger.debug("Writing to existing output file {}", outputFile.getAbsolutePath()); + } + } catch (final @NotNull IOException e) { + Logger.error("Could not create output file {}", outputFile.getAbsolutePath(), e); + return true; + } + + return false; + } + + public void arrangeQosToMatchTopics() { + qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); + } + + public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { + if (mqttVersion == MqttVersion.MQTT_3_1_1) { + if (userProperties != null) { + Logger.warn("Subscribe user properties were set but are unused in MQTT version {}", + MqttVersion.MQTT_3_1_1); + } + } + } + + public void setDefaultOptions() { + final DefaultCLIProperties defaultCLIProperties = Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); + + if (outputFile == null && defaultCLIProperties.getClientSubscribeOutputFile() != null) { + Logger.trace("Setting value of 'toFile' to {}", defaultCLIProperties.getClientSubscribeOutputFile()); + outputFile = new File(defaultCLIProperties.getClientSubscribeOutputFile()); + } + } +} diff --git a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java new file mode 100644 index 000000000..56c62c516 --- /dev/null +++ b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java @@ -0,0 +1,52 @@ +package com.hivemq.cli.commands.options; + +import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; +import com.hivemq.client.mqtt.MqttVersion; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tinylog.Logger; +import picocli.CommandLine; + + +public class UnsubscribeOptions { + + public UnsubscribeOptions() { + } + + public UnsubscribeOptions(final @NotNull String @NotNull [] topics, final @Nullable Mqtt5UserProperty @Nullable [] userProperties) { + this.topics = topics; + this.userProperties = userProperties; + } + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) + @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to publish to") + private @NotNull String @NotNull [] topics; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, + description = "A user property for the unsubscribe message") + private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + + public @Nullable String[] getTopics() { + return topics; + } + + public @Nullable Mqtt5UserProperty[] getUserProperties() { + return userProperties; + } + + public static @NotNull UnsubscribeOptions of(final @NotNull SubscribeOptions subscribeOptions) { + return new UnsubscribeOptions(subscribeOptions.getTopics(), subscribeOptions.getUserPropertiesRaw()); + } + + public void logUnusedUnsubscribeOptions(final @NotNull MqttVersion mqttVersion) { + if (mqttVersion == MqttVersion.MQTT_3_1_1) { + if (userProperties != null) { + Logger.warn( + "Unsubscribe user properties were set but are unused in MQTT Version {}", + MqttVersion.MQTT_3_1_1); + } + } + } +} diff --git a/src/main/java/com/hivemq/cli/commands/AbstractWillFlags.java b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java similarity index 70% rename from src/main/java/com/hivemq/cli/commands/AbstractWillFlags.java rename to src/main/java/com/hivemq/cli/commands/options/WillOptions.java index 3619b90f5..004eeee63 100644 --- a/src/main/java/com/hivemq/cli/commands/AbstractWillFlags.java +++ b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java @@ -1,20 +1,4 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands; +package com.hivemq.cli.commands.options; import com.hivemq.cli.converters.*; import com.hivemq.cli.utils.MqttUtils; @@ -32,160 +16,150 @@ import java.nio.ByteBuffer; import java.util.Arrays; -@CommandLine.Command -public abstract class AbstractWillFlags extends MqttCommand implements Will { +public class WillOptions { @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Wt", "--willTopic"}, description = "The topic of the will message", order = 3) + @CommandLine.Option(names = {"-Wt", "--willTopic"}, description = "The topic of the will message") private @Nullable String willTopic; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wm", "--willMessage"}, converter = ByteBufferConverter.class, - description = "The payload of the will message", order = 3) + description = "The payload of the will message") private @Nullable ByteBuffer willMessage; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wq", "--willQualityOfService"}, defaultValue = "0", converter = MqttQosConverter.class, - description = "Quality of service level for the will message (default: 0)", order = 3) + description = "Quality of service level for the will message (default: 0)") private @Nullable MqttQos willQos; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wr", "--willRetain"}, negatable = true, defaultValue = "false", - description = "Will message as retained message (default: false)", order = 3) + description = "Will message as retained message (default: false)") private boolean willRetain; @SuppressWarnings("unused") @CommandLine.Option(names = {"-We", "--willMessageExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the will message in seconds (default: no message expiry)", order = 3) + description = "The lifetime of the will message in seconds (default: no message expiry)") private @Nullable Long willMessageExpiryInterval; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wd", "--willDelayInterval"}, converter = UnsignedIntConverter.class, description = "The Server delays publishing the client's will message until the will delay has passed (default: " + - Mqtt5WillPublish.DEFAULT_DELAY_INTERVAL + ")", order = 3) + Mqtt5WillPublish.DEFAULT_DELAY_INTERVAL + ")") private @Nullable Long willDelayInterval; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wpf", "--willPayloadFormatIndicator"}, converter = PayloadFormatIndicatorConverter.class, - description = "The payload format indicator of the will message", order = 3) + description = "The payload format indicator of the will message") private @Nullable Mqtt5PayloadFormatIndicator willPayloadFormatIndicator; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wct", "--willContentType"}, - description = "A description of the will message's content", order = 3) + description = "A description of the will message's content") private @Nullable String willContentType; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wrt", "--willResponseTopic"}, - description = "The topic name for the response message", order = 3) + description = "The topic name for the response message") private @Nullable String willResponseTopic; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wcd", "--willCorrelationData"}, converter = ByteBufferConverter.class, - description = "The correlation data of the will message", order = 3) + description = "The correlation data of the will message") private @Nullable ByteBuffer willCorrelationData; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wup", "--willUserProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the will message", order = 3) + description = "A user property of the will message") private @Nullable Mqtt5UserProperty @Nullable [] willUserProperties; - @NotNull String getWillOptions() { - if (willTopic == null) { - return ""; - } - return ", willTopic=" + willTopic + (willQos != null ? (", willQos=" + willQos) : "") + - (willMessage != null ? (", willMessage=" + willMessage) : "") + - //(willRetain != null ? (", willRetain=" + willRetain) : "") + - (willMessageExpiryInterval != null ? (", willMessageExpiryInterval=" + willMessageExpiryInterval) : - "") + (willDelayInterval != null ? (", willDelayInterval=" + willDelayInterval) : "") + - (willPayloadFormatIndicator != null ? (", willPayloadFormatIndicator=" + willPayloadFormatIndicator) : - "") + (willContentType != null ? (", willContentType=" + willContentType) : "") + - (willResponseTopic != null ? (", willResponseTopic=" + willResponseTopic) : "") + - (willCorrelationData != null ? (", willCorrelationData=" + willCorrelationData) : "") + - (willUserProperties != null ? (", willUserProperties=" + Arrays.toString(willUserProperties)) : ""); - } - - void logUnusedOptions() { - if (getVersion() == MqttVersion.MQTT_3_1_1) { - if (willMessageExpiryInterval != null) { - Logger.warn("Will Message Expiry was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (willPayloadFormatIndicator != null) { - Logger.warn("Will Payload Format was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (willDelayInterval != null) { - Logger.warn("Will Delay Interval was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (willContentType != null) { - Logger.warn("Will Content Type was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (willResponseTopic != null) { - Logger.warn("Will Response Topic was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (willCorrelationData != null) { - Logger.warn("Will Correlation Data was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (willUserProperties != null) { - Logger.warn("Will User Properties was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - } - } - - @Override public @Nullable String getWillTopic() { return willTopic; } - @Override public @Nullable ByteBuffer getWillMessage() { return willMessage; } - @Override public @Nullable MqttQos getWillQos() { return willQos; } - @Override public @Nullable Boolean getWillRetain() { return willRetain; } - @Override public @Nullable Long getWillMessageExpiryInterval() { return willMessageExpiryInterval; } - @Override public @Nullable Long getWillDelayInterval() { return willDelayInterval; } - @Override public @Nullable Mqtt5PayloadFormatIndicator getWillPayloadFormatIndicator() { return willPayloadFormatIndicator; } - @Override public @Nullable String getWillContentType() { return willContentType; } - @Override public @Nullable String getWillResponseTopic() { return willResponseTopic; } - @Override public @Nullable ByteBuffer getWillCorrelationData() { return willCorrelationData; } - @Override public @Nullable Mqtt5UserProperties getWillUserProperties() { return MqttUtils.convertToMqtt5UserProperties(willUserProperties); } + + public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { + if (mqttVersion == MqttVersion.MQTT_3_1_1) { + if (willMessageExpiryInterval != null) { + Logger.warn("Will Message Expiry was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (willPayloadFormatIndicator != null) { + Logger.warn("Will Payload Format was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (willDelayInterval != null) { + Logger.warn("Will Delay Interval was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (willContentType != null) { + Logger.warn("Will Content Type was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (willResponseTopic != null) { + Logger.warn("Will Response Topic was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (willCorrelationData != null) { + Logger.warn("Will Correlation Data was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + if (willUserProperties != null) { + Logger.warn("Will User Properties was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); + } + } + } + + @Override + public @NotNull String toString() { + if (willTopic == null) { + return ""; + } else { + return ", willTopic=" + willTopic + (willQos != null ? (", willQos=" + willQos) : "") + + (willMessage != null ? (", willMessage=" + willMessage) : "") + + (willRetain != null ? (", willRetain=" + willRetain) : "") + + (willMessageExpiryInterval != null ? (", willMessageExpiryInterval=" + willMessageExpiryInterval) : "") + + (willDelayInterval != null ? (", willDelayInterval=" + willDelayInterval) : "") + + (willPayloadFormatIndicator != null ? (", willPayloadFormatIndicator=" + willPayloadFormatIndicator) : "") + + (willContentType != null ? (", willContentType=" + willContentType) : "") + + (willResponseTopic != null ? (", willResponseTopic=" + willResponseTopic) : "") + + (willCorrelationData != null ? (", willCorrelationData=" + willCorrelationData) : "") + + (willUserProperties != null ? (", willUserProperties=" + Arrays.toString(willUserProperties)) : ""); + } + } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java index 5c092a67e..58a8493d6 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java @@ -16,17 +16,17 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.CliCommand; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.inject.Inject; +import java.util.concurrent.Callable; /** * Command that clears the screen. */ @CommandLine.Command(name = "cls", aliases = "clear", description = "Clear the screen") -public class ClearScreenCommand implements CliCommand, Runnable { +public class ClearScreenCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") @@ -36,22 +36,13 @@ public class ClearScreenCommand implements CliCommand, Runnable { ClearScreenCommand() {} @Override - public void run() { + public Integer call() { ShellCommand.clearScreen(); + return 0; } @Override public @NotNull String toString() { return this.getClass().getSimpleName(); } - - @Override - public boolean isVerbose() { - return ShellCommand.isVerbose(); - } - - @Override - public boolean isDebug() { - return ShellCommand.isDebug(); - } } \ No newline at end of file diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java index b36a0b2c3..dfe9118c9 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java @@ -17,48 +17,26 @@ package com.hivemq.cli.commands.shell; import com.google.common.base.Throwables; -import com.hivemq.cli.commands.Disconnect; -import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; -import com.hivemq.cli.converters.UnsignedIntConverter; +import com.hivemq.cli.commands.options.DisconnectOptions; +import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.util.Arrays; -import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "dis", aliases = "disconnect", description = "Disconnects this MQTT client") -public class ContextDisconnectCommand extends ShellContextCommand implements Runnable, Disconnect { +public class ContextDisconnectCommand extends ShellContextCommand implements Callable { @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") + @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") private boolean usageHelpRequested; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-a", "--all"}, defaultValue = "false", - description = "Disconnect all connected clients") - private boolean disconnectAll; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-e", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The session expiry of the disconnect (default: 0)") - private @Nullable Long sessionExpiryInterval; + @CommandLine.Mixin + private final @NotNull DisconnectOptions disconnectOptions = new DisconnectOptions(); - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--reason"}, description = "The reason of the disconnect") - private @Nullable String reasonString; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the disconnect message") - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; @SuppressWarnings("unused") //needed for pico cli - reflection code generation public ContextDisconnectCommand() { @@ -72,58 +50,34 @@ public ContextDisconnectCommand(final @NotNull MqttClientExecutor executor) { } @Override - public void run() { + public Integer call() { + Logger.trace("Command {} ", this); - logUnusedDisconnectOptions(); + if (contextClient != null) { + disconnectOptions.logUnusedDisconnectOptions(contextClient.getConfig().getMqttVersion()); + } try { - if (disconnectAll) { - mqttClientExecutor.disconnectAllClients(this); - } else { - mqttClientExecutor.disconnect(this); + if (disconnectOptions.isDisconnectAll()) { + mqttClientExecutor.disconnectAllClients(disconnectOptions); + } else if (disconnectOptions.getClientIdentifier() != null && disconnectOptions.getHost() != null) { + final ClientKey clientKey = ClientKey.of(disconnectOptions.getClientIdentifier(), disconnectOptions.getHost()); + mqttClientExecutor.disconnect(clientKey, disconnectOptions); + } else if (contextClient != null) { + mqttClientExecutor.disconnect(contextClient, disconnectOptions); } } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + Logger.error(ex, "Unable to disconnect: {}" ,Throwables.getRootCause(ex).getMessage()); + return 1; } - } - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "key=" + getKey() + ", all=" + disconnectAll + - (sessionExpiryInterval != null ? (", sessionExpiryInterval=" + sessionExpiryInterval) : "") + - (reasonString != null ? (", reasonString=" + reasonString) : "") + - (userProperties != null ? (", userProperties=" + Arrays.toString(userProperties)) : "") + "}"; + return 0; } @Override - public @Nullable Long getSessionExpiryInterval() { - return sessionExpiryInterval; - } - - @Override - public @Nullable String getReasonString() { - return reasonString; - } - - @Override - public @Nullable Mqtt5UserProperties getUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(userProperties); + public @NotNull String toString() { + return getClass().getSimpleName() + disconnectOptions; } - private void logUnusedDisconnectOptions() { - if (Objects.requireNonNull(contextClient).getConfig().getMqttVersion() == MqttVersion.MQTT_3_1_1) { - if (sessionExpiryInterval != null) { - Logger.warn("Session expiry interval set but is unused in Mqtt version {}", MqttVersion.MQTT_3_1_1); - } - - if (reasonString != null) { - Logger.warn("Reason string was set but is unused in Mqtt version {}", MqttVersion.MQTT_3_1_1); - } - - if (userProperties != null) { - Logger.warn("User properties were set but are unused in Mqtt version {}", MqttVersion.MQTT_3_1_1); - } - } - } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java index ce590c1f9..d675f37c5 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java @@ -21,9 +21,10 @@ import picocli.CommandLine; import javax.inject.Inject; +import java.util.concurrent.Callable; @CommandLine.Command(name = "exit", description = "Exit the current context") -public class ContextExitCommand extends ShellContextCommand implements Runnable { +public class ContextExitCommand extends ShellContextCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") @@ -41,8 +42,9 @@ public ContextExitCommand(final @NotNull MqttClientExecutor mqttClientExecutor) } @Override - public void run() { + public Integer call() { removeContext(); + return 0; } @Override diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java index 8c1d8eed8..6f42d4752 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java @@ -17,80 +17,24 @@ package com.hivemq.cli.commands.shell; import com.google.common.base.Throwables; -import com.hivemq.cli.commands.Publish; -import com.hivemq.cli.commands.options.MessagePayloadOptions; -import com.hivemq.cli.converters.*; +import com.hivemq.cli.commands.options.PublishOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.datatypes.MqttQos; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; -import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PayloadFormatIndicator; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "pub", aliases = "publish", description = "Publish a message to a list of topics") -public class ContextPublishCommand extends ShellContextCommand implements Runnable, Publish { +public class ContextPublishCommand extends ShellContextCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") boolean usageHelpRequested; - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required - @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to publish to") - private @NotNull String @NotNull [] topics; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "0", - description = "Quality of service for the corresponding topic (default for all: 0)") - private @NotNull MqttQos @NotNull [] qos; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required - @CommandLine.ArgGroup(multiplicity = "1") - private @NotNull MessagePayloadOptions message; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--retain"}, negatable = true, defaultValue = "false", - description = "The message will be retained (default: false)") - private boolean retain; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-e", "--messageExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the publish message in seconds (default: no message expiry)") - private @Nullable Long messageExpiryInterval; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-pf", "--payloadFormatIndicator"}, converter = PayloadFormatIndicatorConverter.class, - description = "The payload format indicator of the publish message") - private @Nullable Mqtt5PayloadFormatIndicator payloadFormatIndicator; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-ct", "--contentType"}, description = "A description of publish message's content") - private @Nullable String contentType; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-rt", "--responseTopic"}, - description = "The topic name for the publish message`s response message") - private @Nullable String responseTopic; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-cd", "--correlationData"}, converter = ByteBufferConverter.class, - description = "The correlation data of the publish message") - private @Nullable ByteBuffer correlationData; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the publish message") - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + @CommandLine.Mixin + private final @NotNull PublishOptions publishOptions = new PublishOptions(); @SuppressWarnings("unused") //needed for pico cli - reflection code generation public ContextPublishCommand() { @@ -104,105 +48,29 @@ public ContextPublishCommand(final @NotNull MqttClientExecutor executor) { } @Override - public void run() { - logUnusedOptions(); + public Integer call() { Logger.trace("Command {} ", this); - try { - qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); - mqttClientExecutor.publish(Objects.requireNonNull(contextClient), this); - } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); - } - } - - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "key=" + getKey() + ", topics=" + Arrays.toString(topics) + ", qos=" + - Arrays.toString(qos) + ", message=" + - new String(message.getMessageBuffer().array(), StandardCharsets.UTF_8) + ", retain=" + retain + - (messageExpiryInterval != null ? (", messageExpiryInterval=" + messageExpiryInterval) : "") + - (payloadFormatIndicator != null ? (", payloadFormatIndicator=" + payloadFormatIndicator) : "") + - (contentType != null ? (", contentType=" + contentType) : "") + - (responseTopic != null ? (", responseTopic=" + responseTopic) : "") + (correlationData != null ? - (", correlationData=" + new String(correlationData.array(), StandardCharsets.UTF_8)) : "") + - (userProperties != null ? (", userProperties=" + Arrays.toString(userProperties)) : "") + '}'; - } - - @Override - public @NotNull String @NotNull [] getTopics() { - return topics; - } + if (contextClient != null) { + publishOptions.logUnusedOptions(contextClient.getConfig().getMqttVersion()); - @Override - public @NotNull MqttQos @NotNull [] getQos() { - return qos; - } - - @Override - public @NotNull ByteBuffer getMessage() { - return message.getMessageBuffer(); - } - - @Override - public @Nullable Boolean getRetain() { - return retain; - } - - @Override - public @Nullable Long getMessageExpiryInterval() { - return messageExpiryInterval; - } - - @Override - public @Nullable Mqtt5PayloadFormatIndicator getPayloadFormatIndicator() { - return payloadFormatIndicator; - } - - @Override - public @Nullable String getContentType() { - return contentType; - } - - @Override - public @Nullable String getResponseTopic() { - return responseTopic; - } + try { + publishOptions.arrangeQosToMatchTopics(); + mqttClientExecutor.publish(contextClient, publishOptions); + } catch (final Exception ex) { + Logger.error(ex, "Unable to publish: {}", Throwables.getRootCause(ex).getMessage()); + return 1; + } + } - @Override - public @Nullable ByteBuffer getCorrelationData() { - return correlationData; + return 0; } @Override - public @Nullable Mqtt5UserProperties getUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(userProperties); + public String toString() { + return "ContextPublishCommand{" + "publishOptions=" + + publishOptions + '}'; } - private void logUnusedOptions() { - if (Objects.requireNonNull(contextClient).getConfig().getMqttVersion() == MqttVersion.MQTT_3_1_1) { - if (messageExpiryInterval != null) { - Logger.warn("Publish message expiry was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (payloadFormatIndicator != null) { - Logger.warn("Publish payload format indicator was set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - if (contentType != null) { - Logger.warn("Publish content type was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (responseTopic != null) { - Logger.warn("Publish response topic was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); - } - if (correlationData != null) { - Logger.warn("Publish correlation data was set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - if (userProperties != null) { - Logger.warn("Publish user properties were set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - } - } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java index f0e695023..58676f8a6 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java @@ -17,129 +17,87 @@ package com.hivemq.cli.commands.shell; import com.google.common.base.Throwables; -import com.hivemq.cli.DefaultCLIProperties; -import com.hivemq.cli.commands.Subscribe; -import com.hivemq.cli.commands.Unsubscribe; -import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; -import com.hivemq.cli.converters.MqttQosConverter; +import com.hivemq.cli.commands.options.SubscribeOptions; +import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.datatypes.MqttQos; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.io.File; -import java.util.Arrays; import java.util.Objects; import java.util.Scanner; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @CommandLine.Command(name = "sub", aliases = "subscribe", description = "Subscribe this MQTT client to a list of topics") -public class ContextSubscribeCommand extends ShellContextCommand implements Runnable, Subscribe, Unsubscribe { +public class ContextSubscribeCommand extends ShellContextCommand implements Callable { private static final int IDLE_TIME = 1000; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required - @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to subscribe to") - private @NotNull String @NotNull [] topics; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "2", - description = "Quality of service for the corresponding topics (default for all: 2)") - private @NotNull MqttQos @NotNull [] qos; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the subscribe message") - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + @CommandLine.Mixin + private final @NotNull SubscribeOptions subscribeOptions = new SubscribeOptions(); - @CommandLine.Option(names = {"-of", "--outputToFile"}, - description = "A file to which the received publish messages will be written") - private @Nullable File outputFile; - - @CommandLine.Option(names = {"-oc", "--outputToConsole"}, defaultValue = "false", - description = "The received messages will be written to the console (default: false)") - private boolean printToSTDOUT; + @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") + boolean usageHelpRequested; @SuppressWarnings("unused") @CommandLine.Option(names = {"-s", "--stay"}, defaultValue = "false", description = "The subscribe will block the console and wait for publish messages to print (default: false)") private boolean stay; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-b64", "--base64"}, - description = "Specify the encoding of the received messages as Base64 (default: false)") - private boolean base64; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-J", "--jsonOutput"}, defaultValue = "false", - description = "Print the received publishes in pretty JSON format", order = 1) - private boolean jsonOutput; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-T", "--showTopics"}, defaultValue = "false", - description = "Prepend the specific topic name to the received publish", order = 1) - private boolean showTopics; - - private final @NotNull DefaultCLIProperties defaultCLIProperties; - @SuppressWarnings("unused") //needed for pico cli - reflection code generation public ContextSubscribeCommand() { //noinspection ConstantConditions - this(null, null); + this(null); } @Inject - public ContextSubscribeCommand( - final @NotNull MqttClientExecutor mqttClientExecutor, - final @NotNull DefaultCLIProperties defaultCLIProperties) { + public ContextSubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { super(mqttClientExecutor); - this.defaultCLIProperties = defaultCLIProperties; } @Override - public void run() { + public Integer call() { + Logger.trace("Command {} ", this); - setDefaultOptions(); + if (contextClient == null) { + Logger.error("The client to subscribe with does not exist"); + return 1; + } - logUnusedOptions(); + subscribeOptions.setDefaultOptions(); + subscribeOptions.logUnusedOptions(contextClient.getConfig().getMqttVersion()); if (stay) { - printToSTDOUT = true; + subscribeOptions.setPrintToSTDOUT(true); } - if (outputFileInvalid(outputFile)) { - return; + if (subscribeOptions.isOutputFileInvalid(subscribeOptions.getOutputFile())) { + return 1; } try { - qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); - mqttClientExecutor.subscribe(Objects.requireNonNull(contextClient), this); + subscribeOptions.arrangeQosToMatchTopics(); + mqttClientExecutor.subscribe(Objects.requireNonNull(contextClient), subscribeOptions); } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + Logger.error(ex, "Unable to subscribe: {}", Throwables.getRootCause(ex).getMessage()); } if (stay) { try { stay(); } catch (final InterruptedException ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + Logger.error(ex, "Interrupted while staying after subscribe: {}",Throwables.getRootCause(ex).getMessage()); + return 1; } } + + return 0; } private void stay() throws InterruptedException { @@ -175,70 +133,14 @@ private void stay() throws InterruptedException { if (!contextClient.getState().isConnectedOrReconnect()) { removeContext(); } else { - mqttClientExecutor.unsubscribe(contextClient, this); + mqttClientExecutor.unsubscribe(contextClient, UnsubscribeOptions.of(subscribeOptions)); } } } @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "key=" + ((contextClient == null) ? "null" : getKey()) + ", topics=" + - Arrays.toString(topics) + ", qos=" + Arrays.toString(qos) + ", outputToConsole=" + printToSTDOUT + - ", base64=" + base64 + ", jsonOutput=" + jsonOutput + ", showTopics=" + showTopics + - (userProperties != null ? (", userProperties=" + Arrays.toString(userProperties)) : "") + - (outputFile != null ? (", publishFile=" + outputFile.getAbsolutePath()) : "") + '}'; - } - - @Override - public @NotNull String @NotNull [] getTopics() { - return topics; - } - - @Override - public @NotNull MqttQos @NotNull [] getQos() { - return qos; + public String toString() { + return "ContextSubscribeCommand{" + "subscribeOptions=" + subscribeOptions + ", stay=" + stay + '}'; } - @Override - public @Nullable File getOutputFile() { - return outputFile; - } - - @Override - public boolean isPrintToSTDOUT() { - return printToSTDOUT; - } - - @Override - public boolean isBase64() {return base64;} - - @Override - public boolean isJsonOutput() {return jsonOutput;} - - @Override - public boolean showTopics() {return showTopics;} - - @Override - public @Nullable Mqtt5UserProperties getUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(userProperties); - } - - private void setDefaultOptions() { - if (outputFile == null && defaultCLIProperties.getClientSubscribeOutputFile() != null) { - if (isVerbose()) { - Logger.trace("Setting value of 'toFile' to {}", defaultCLIProperties.getClientSubscribeOutputFile()); - } - outputFile = new File(defaultCLIProperties.getClientSubscribeOutputFile()); - } - - } - - private void logUnusedOptions() { - if (Objects.requireNonNull(contextClient).getConfig().getMqttVersion() == MqttVersion.MQTT_3_1_1) { - if (userProperties != null) { - Logger.warn("Subscribe user properties were set but are unused in MQTT version {}", - MqttVersion.MQTT_3_1_1); - } - } - } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java index 892016edf..ac1ae8c2b 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java @@ -17,7 +17,7 @@ package com.hivemq.cli.commands.shell; import com.google.common.base.Throwables; -import com.hivemq.cli.commands.Context; +import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; import org.jetbrains.annotations.NotNull; @@ -26,10 +26,10 @@ import picocli.CommandLine; import javax.inject.Inject; -import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "switch", description = "Switch the current context") -public class ContextSwitchCommand extends ShellContextCommand implements Runnable, Context { +public class ContextSwitchCommand extends ShellContextCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") @@ -59,30 +59,34 @@ public ContextSwitchCommand(final @NotNull MqttClientExecutor mqttClientExecutor } @Override - public void run() { + public Integer call() { + + Logger.trace("Command {} ", this); + if (contextName == null && identifier == null) { ShellCommand.usage(this); - return; + return 0; } if (contextName != null) { try { extractKeyFromContextName(contextName); } catch (final IllegalArgumentException ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); - return; + Logger.error(ex, "Unable to switch context: {}", Throwables.getRootCause(ex).getMessage()); + return 1; } } - Logger.trace("Command {} ", this); - - final MqttClient client = mqttClientExecutor.getMqttClient(this); + final MqttClient client = mqttClientExecutor.getMqttClient(ClientKey.of(identifier, host)); if (client != null) { updateContext(client); } else { Logger.error("Context {}@{} not found", identifier, host); + return 1; } + + return 0; } private void extractKeyFromContextName(final String contextName) { @@ -98,18 +102,9 @@ private void extractKeyFromContextName(final String contextName) { } } - @Override - public @NotNull String getKey() { - return "client {" + "identifier='" + getIdentifier() + '\'' + ", host='" + host + '\'' + '}'; - } - @Override public @NotNull String toString() { return getClass().getSimpleName() + "{" + "identifier=" + identifier + ", host=" + host + '}'; } - @Override - public @NotNull String getIdentifier() { - return Objects.requireNonNull(identifier); - } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java index b1e537a23..717a78d3f 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java @@ -17,38 +17,26 @@ package com.hivemq.cli.commands.shell; import com.google.common.base.Throwables; -import com.hivemq.cli.commands.Unsubscribe; -import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; +import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.MqttVersion; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.util.Arrays; -import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "unsub", aliases = "unsubscribe", description = "Unsubscribe this MQTT client from a list of topics") -public class ContextUnsubscribeCommand extends ShellContextCommand implements Runnable, Unsubscribe { +public class ContextUnsubscribeCommand extends ShellContextCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") private boolean usageHelpRequested; - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required - @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to publish to") - private @NotNull String @NotNull [] topics; + @CommandLine.Mixin + private final @NotNull UnsubscribeOptions unsubscribeOptions = new UnsubscribeOptions(); - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property for the unsubscribe message") - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; @SuppressWarnings("unused") //needed for pico cli - reflection code generation public ContextUnsubscribeCommand() { @@ -62,41 +50,24 @@ public ContextUnsubscribeCommand(final @NotNull MqttClientExecutor mqttClientExe } @Override - public void run() { + public Integer call() { + Logger.trace("Command {} ", this); - logUnusedUnsubscribeOptions(); + if (contextClient == null) { + Logger.error("The client to unsubscribe with does not exist"); + return 1; + } + + unsubscribeOptions.logUnusedUnsubscribeOptions(contextClient.getConfig().getMqttVersion()); try { - mqttClientExecutor.unsubscribe(Objects.requireNonNull(contextClient), this); + mqttClientExecutor.unsubscribe(contextClient, unsubscribeOptions); } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + Logger.error(ex, "Unable to unsubscribe: {}", Throwables.getRootCause(ex).getMessage()); + return 1; } - } - - private void logUnusedUnsubscribeOptions() { - if (Objects.requireNonNull(contextClient).getConfig().getMqttVersion() == MqttVersion.MQTT_3_1_1) { - if (userProperties != null) { - Logger.warn( - "Unsubscribe user properties were set but are unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - } - } - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "key=" + getKey() + ", topics=" + Arrays.toString(topics) + - (userProperties != null ? (", userProperties=" + Arrays.toString(userProperties)) : "") + '}'; - } - - @Override - public @NotNull String @NotNull [] getTopics() { - return topics; - } - - @Override - public @Nullable Mqtt5UserProperties getUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(userProperties); + return 0; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java index 0f2bc35fb..da50c3bfd 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.CliCommand; import com.hivemq.cli.mqtt.ClientData; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; @@ -28,11 +27,12 @@ import java.io.PrintWriter; import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.Callable; import java.util.stream.Collectors; @CommandLine.Command(name = "ls", aliases = "list", description = "List all connected clients with their respective identifiers") -public class ListClientsCommand implements Runnable, CliCommand { +public class ListClientsCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") @@ -66,7 +66,8 @@ public ListClientsCommand() { } @Override - public void run() { + public Integer call() { + Logger.trace("Command {}", this); final List sortedClientData = getSortedClientData(); @@ -77,7 +78,7 @@ public void run() { Objects.requireNonNull(writer).println("total " + sortedClientData.size()); if (sortedClientData.size() == 0) { - return; + return 0; } final Set clients = @@ -148,6 +149,8 @@ public void run() { } } } + + return 0; } @Override @@ -156,16 +159,6 @@ public void run() { ", reverse=" + reverse + ", listSubscriptions" + listSubscriptions + ", longOutput=" + longOutput + '}'; } - @Override - public boolean isVerbose() { - return ShellCommand.isVerbose(); - } - - @Override - public boolean isDebug() { - return ShellCommand.isDebug(); - } - public @NotNull List getSortedClientData() { final List sortedClientData = new ArrayList<>(MqttClientExecutor.getClientDataMap().values()); diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index ee0a0d1c6..7faca16ad 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -17,47 +17,31 @@ package com.hivemq.cli.commands.shell; import com.google.common.base.Throwables; -import com.hivemq.cli.commands.AbstractCommonFlags; -import com.hivemq.cli.commands.Connect; -import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; -import com.hivemq.cli.converters.UnsignedIntConverter; +import com.hivemq.cli.commands.options.ConnectOptions; +import com.hivemq.cli.commands.options.DebugOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; -import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.MqttClient; -import com.hivemq.client.mqtt.MqttClientSslConfig; -import com.hivemq.client.mqtt.MqttVersion; import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.util.Arrays; +import java.util.concurrent.Callable; @CommandLine.Command(name = "con", aliases = "connect", description = "Connect an MQTT client", abbreviateSynopsis = true) -public class ShellConnectCommand extends AbstractCommonFlags implements Runnable, Connect { +public class ShellConnectCommand implements Callable { + + private final @NotNull MqttClientExecutor mqttClientExecutor; @SuppressWarnings("unused") @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") boolean usageHelpRequested; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-se", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the session of the connected client'") - private @Nullable Long sessionExpiryInterval; + @CommandLine.Mixin + private final @NotNull ConnectOptions connectOptions = new ConnectOptions(); - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the connect message") - private @Nullable Mqtt5UserProperty @Nullable [] connectUserProperties; - - private final @NotNull MqttClientExecutor mqttClientExecutor; - - private @Nullable MqttClientSslConfig sslConfig; @SuppressWarnings("unused") //needed for pico cli - reflection code generation public ShellConnectCommand() { @@ -70,86 +54,31 @@ public ShellConnectCommand(final @NotNull MqttClientExecutor mqttClientExecutor) this.mqttClientExecutor = mqttClientExecutor; } - public void run() { - setDefaultOptions(); - - try { - sslConfig = buildSslConfig(); - } catch (final Exception e) { - Logger.error(e, "Could not build SSL configuration"); - return; - } - - logUnusedOptions(); - final MqttClient client = connect(); - - sslConfig = null; - ShellContextCommand.updateContext(client); - } + public Integer call() { - private @Nullable MqttClient connect() { Logger.trace("Command {} ", this); + connectOptions.setDefaultOptions(); + + final MqttClient client; try { - return mqttClientExecutor.connect(this); + client = mqttClientExecutor.connect(connectOptions); } catch (final ConnectionFailedException cex) { - Logger.error(cex, cex.getCause().getMessage()); + Logger.error(cex, "Unable to connect: {}", cex.getCause().getMessage()); + return 1; } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); - } - return null; - } - - @Override - public void logUnusedOptions() { - super.logUnusedOptions(); - if (getVersion() == MqttVersion.MQTT_3_1_1) { - if (sessionExpiryInterval != null) { - Logger.warn("Connect session expiry interval was set but is unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } - - if (connectUserProperties != null) { - Logger.warn("Connect user properties were set but are unused in MQTT Version {}", - MqttVersion.MQTT_3_1_1); - } + Logger.error(ex, "Unable to connect: {}", Throwables.getRootCause(ex).getMessage()); + return 1; } - } - - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + connectOptions() + "}"; - } - - @Override - public boolean isDebug() { - return ShellCommand.isDebug(); - } - @Override - public boolean isVerbose() { - return ShellCommand.isVerbose(); - } - - @Override - public @Nullable Long getSessionExpiryInterval() { - return sessionExpiryInterval; - } + ShellContextCommand.updateContext(client); - @Override - public @Nullable Mqtt5UserProperties getConnectUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(connectUserProperties); + return 0; } @Override - public @Nullable MqttClientSslConfig getSslConfig() { - return sslConfig; + public @NotNull String toString() { + return getClass().getSimpleName() + "{" + connectOptions + "}"; } - private @NotNull String connectOptions() { - return commonOptions() + - (sessionExpiryInterval != null ? (", sessionExpiryInterval=" + sessionExpiryInterval) : "") + - (connectUserProperties != null ? (", userProperties=" + Arrays.toString(connectUserProperties)) : "") + - connectRestrictionOptions(); - } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java index 743c3cf56..3839c8705 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.CliCommand; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; import org.jetbrains.annotations.NotNull; @@ -25,13 +24,14 @@ import javax.inject.Inject; import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(sortOptions = false, name = "> ", description = "In context mode all MQTT commands relate to the currently active client.", synopsisHeading = "%n@|bold Usage|@: ", synopsisSubcommandLabel = "{ pub | sub | unsub | dis | switch | ls | cls | exit }", descriptionHeading = "%n", optionListHeading = "%n@|bold Options|@:%n", commandListHeading = "%n@|bold Commands|@:%n", separator = " ") -public class ShellContextCommand implements Runnable, CliCommand { +public class ShellContextCommand implements Callable { public static @Nullable MqttClient contextClient; @@ -61,31 +61,9 @@ public static void removeContext() { } @Override - public void run() { + public Integer call() { Objects.requireNonNull(ShellCommand.TERMINAL_WRITER).println(ShellCommand.getUsageMessage()); + return 0; } - public @NotNull String getKey() { - return "client {" + "identifier='" + Objects.requireNonNull(contextClient) - .getConfig() - .getClientIdentifier() - .map(Object::toString) - .orElse("") + '\'' + ", host='" + contextClient.getConfig().getServerHost() + '\'' + '}'; - } - - public @NotNull String getIdentifier() { - return Objects.requireNonNull(contextClient) - .getConfig() - .getClientIdentifier() - .map(Object::toString) - .orElse(""); - } - - public boolean isDebug() { - return ShellCommand.isDebug(); - } - - public boolean isVerbose() { - return ShellCommand.isVerbose(); - } -} +} \ No newline at end of file diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java index 0234f7cc7..057665032 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java @@ -18,57 +18,25 @@ import com.google.common.base.Throwables; import com.hivemq.cli.DefaultCLIProperties; -import com.hivemq.cli.commands.Disconnect; -import com.hivemq.cli.commands.MqttAction; -import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; -import com.hivemq.cli.converters.UnsignedIntConverter; +import com.hivemq.cli.commands.options.DisconnectOptions; +import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; -import com.hivemq.cli.utils.MqttUtils; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; -import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import picocli.CommandLine; import javax.inject.Inject; -import java.util.Arrays; -import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "dis", aliases = "disconnect", description = "Disconnect an MQTT client") -public class ShellDisconnectCommand implements MqttAction, Disconnect { +public class ShellDisconnectCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") boolean usageHelpRequested; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-a", "--all"}, defaultValue = "false", - description = "Disconnect all connected clients") - private boolean disconnectAll; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-i", "--identifier"}, - description = "The client identifier UTF-8 String (default randomly generated string)") - private @Nullable String identifier; - - @CommandLine.Option(names = {"-h", "--host"}, - description = "The hostname of the message broker (default 'localhost')") - private @Nullable String host; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-e", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The session expiry of the disconnect (default: 0)") - private @Nullable Long sessionExpiryInterval; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--reason"}, description = "The reason of the disconnect") - private @Nullable String reasonString; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the disconnect message") - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + @CommandLine.Mixin + private final @NotNull DisconnectOptions disconnectOptions = new DisconnectOptions(); private final @NotNull MqttClientExecutor mqttClientExecutor; private final @NotNull DefaultCLIProperties defaultCLIProperties; @@ -88,73 +56,34 @@ public ShellDisconnectCommand() { } @Override - public void run() { - if (host == null) { - host = defaultCLIProperties.getHost(); + public Integer call() { + if (disconnectOptions.getHost() == null) { + disconnectOptions.setHost(defaultCLIProperties.getHost()); } Logger.trace("Command {} ", this); try { - if (disconnectAll) { - mqttClientExecutor.disconnectAllClients(this); + if (disconnectOptions.isDisconnectAll()) { + mqttClientExecutor.disconnectAllClients(disconnectOptions); } else { - if (identifier == null) { + if (disconnectOptions.getClientIdentifier() == null) { Logger.error("Missing required option '--identifier='"); - return; + return 1; } - mqttClientExecutor.disconnect(this); + final ClientKey clientKey = ClientKey.of(disconnectOptions.getClientIdentifier(), disconnectOptions.getHost()); + mqttClientExecutor.disconnect(clientKey, disconnectOptions); } } catch (final Exception ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + Logger.error(ex, "Unable to disconnect: {}", Throwables.getRootCause(ex).getMessage()); + return 1; } - } - - @Override - public @NotNull String getIdentifier() { - return Objects.requireNonNull(identifier); - } - - @Override - public @NotNull String getKey() { - return "client {" + "identifier='" + getIdentifier() + '\'' + ", host='" + getHost() + '\'' + '}'; - } - - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "disconnectAll=" + disconnectAll + - (identifier != null ? (", identifier=" + identifier) : "") + (host != null ? (", host=" + host) : "") + - (sessionExpiryInterval != null ? (", sessionExpiryInterval=" + host) : "") + - (reasonString != null ? (", reasonString=" + reasonString) : "") + - (userProperties != null ? (", userProperties=" + Arrays.toString(userProperties)) : "") + "}"; - - } - - @Override - public @Nullable Long getSessionExpiryInterval() { - return sessionExpiryInterval; - } - @Override - public @Nullable String getReasonString() { - return reasonString; - } - - public @NotNull String getHost() { - return Objects.requireNonNull(host); - } - - @Override - public @Nullable Mqtt5UserProperties getUserProperties() { - return MqttUtils.convertToMqtt5UserProperties(userProperties); + return 0; } @Override - public boolean isVerbose() { - return ShellCommand.isVerbose(); + public String toString() { + return "ShellDisconnectCommand{" + "disconnectOptions=" + disconnectOptions + '}'; } - @Override - public boolean isDebug() { - return ShellCommand.isDebug(); - } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java index 2643279d3..d93d3d6dd 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java @@ -16,14 +16,14 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.CliCommand; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.inject.Inject; +import java.util.concurrent.Callable; @CommandLine.Command(name = "exit", description = "Exit the shell") -public class ShellExitCommand implements Runnable, CliCommand { +public class ShellExitCommand implements Callable { @SuppressWarnings("unused") @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") @@ -34,8 +34,9 @@ public ShellExitCommand() { } @Override - public void run() { + public Integer call() { ShellCommand.exitShell(); + return 0; } @Override @@ -43,14 +44,5 @@ public void run() { return getClass().getSimpleName(); } - @Override - public boolean isVerbose() { - return ShellCommand.isVerbose(); - } - - @Override - public boolean isDebug() { - return ShellCommand.isDebug(); - } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java index 57b0b2048..a931febfa 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java @@ -21,10 +21,11 @@ import picocli.CommandLine; import javax.inject.Inject; +import java.util.concurrent.Callable; @CommandLine.Command(name = "version", description = "Prints version information", versionProvider = MqttCLIMain.CLIVersionProvider.class) -public class VersionCommand implements Runnable { +public class VersionCommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec @@ -35,7 +36,8 @@ public class VersionCommand implements Runnable { } @Override - public void run() { + public Integer call() { spec.commandLine().printVersionHelp(System.out); + return 0; } } diff --git a/src/main/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutor.java b/src/main/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutor.java index 5d307a370..d03b7478f 100644 --- a/src/main/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutor.java +++ b/src/main/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutor.java @@ -16,9 +16,7 @@ package com.hivemq.cli.mqtt; -import com.hivemq.cli.commands.*; -import com.hivemq.cli.commands.cli.PublishCommand; -import com.hivemq.cli.commands.cli.SubscribeCommand; +import com.hivemq.cli.commands.options.*; import com.hivemq.cli.utils.IntersectionUtil; import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.MqttClient; @@ -59,58 +57,76 @@ abstract class AbstractMqttClientExecutor { private static final @NotNull Map clientKeyToClientData = new ConcurrentHashMap<>(); abstract void mqtt5Connect( - final @NotNull Mqtt5Client client, - final @NotNull Mqtt5Connect connectMessage, - final @NotNull Connect connect); + final @NotNull Mqtt5Client client, final @NotNull Mqtt5Connect connectMessage); abstract void mqtt3Connect( - final @NotNull Mqtt3Client client, - final @NotNull Mqtt3Connect connectMessage, - final @NotNull Connect connect); + final @NotNull Mqtt3Client client, final @NotNull Mqtt3Connect connectMessage); abstract void mqtt5Subscribe( final @NotNull Mqtt5Client client, - final @NotNull Subscribe subscribe, + final @NotNull SubscribeOptions subscribeOptions, final @NotNull String topic, final @NotNull MqttQos qos); abstract void mqtt3Subscribe( final @NotNull Mqtt3Client client, - final @NotNull Subscribe subscribe, + final @NotNull SubscribeOptions subscribeOptions, final @NotNull String topic, final @NotNull MqttQos qos); abstract void mqtt5Publish( final @NotNull Mqtt5Client client, - final @NotNull Publish publish, + final @NotNull PublishOptions publishOptions, final @NotNull String topic, final @NotNull MqttQos qos); abstract void mqtt3Publish( final @NotNull Mqtt3Client client, - final @NotNull Publish publish, + final @NotNull PublishOptions publishOptions, final @NotNull String topic, final @NotNull MqttQos qos); - abstract void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull Unsubscribe unsubscribe); + abstract void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull UnsubscribeOptions unsubscribeOptions); - abstract void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull Unsubscribe unsubscribe); + abstract void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull UnsubscribeOptions unsubscribeOptions); - abstract void mqtt5Disconnect(final @NotNull Mqtt5Client client, final @NotNull Disconnect disconnect); + abstract void mqtt5Disconnect( + final @NotNull Mqtt5Client client, + final @NotNull DisconnectOptions disconnectOptions); - abstract void mqtt3Disconnect(final @NotNull Mqtt3Client client, final @NotNull Disconnect disconnect); + abstract void mqtt3Disconnect( + final @NotNull Mqtt3Client client, + final @NotNull DisconnectOptions disconnectOptions); - public @NotNull MqttClient subscribe(final @NotNull SubscribeCommand subscribeCommand) { - final MqttClient client = connect(subscribeCommand); + public @NotNull MqttClient connect(final @NotNull ConnectOptions connectOptions) throws Exception { + return connect(connectOptions, null); + } - subscribe(client, subscribeCommand); + public @NotNull MqttClient connect( + final @NotNull ConnectOptions connectOptions, + final @Nullable SubscribeOptions subscribeOptions) throws Exception { - return client; + final String clientKey = ClientKey.of(connectOptions.getIdentifier(), connectOptions.getHost()).toString(); + if (isConnected(clientKey)) { + Logger.debug("Client is already connected ({})", clientKey); + Logger.info("Using already connected ({})", clientKey); + return clientKeyToClientData.get(clientKey).getClient(); + } + + switch (connectOptions.getVersion()) { + case MQTT_5_0: + return connectMqtt5Client(connectOptions, subscribeOptions); + case MQTT_3_1_1: + return connectMqtt3Client(connectOptions, subscribeOptions); + } + + throw new IllegalStateException( + "The MQTT Version specified is not supported. Version was " + connectOptions.getVersion()); } - public void subscribe(final @NotNull MqttClient client, final @NotNull Subscribe subscribe) { - for (int i = 0; i < subscribe.getTopics().length; i++) { - final String topic = subscribe.getTopics()[i]; + public void subscribe(final @NotNull MqttClient client, final @NotNull SubscribeOptions subscribeOptions) { + for (int i = 0; i < subscribeOptions.getTopics().length; i++) { + final String topic = subscribeOptions.getTopics()[i]; final String key = MqttUtils.buildKey(client.getConfig().getClientIdentifier().map(Object::toString).orElse(""), client.getConfig().getServerHost()); @@ -118,24 +134,25 @@ public void subscribe(final @NotNull MqttClient client, final @NotNull Subscribe // This check only works as subscribes are implemented blocking. // Otherwise, we would need to check the topics before they are iterated as they are added to the client data after a successful subscribe. final List intersectingFilters = - checkForSharedTopicDuplicate(clientKeyToClientData.get(key).getSubscribedTopics(), + checkForSharedTopicDuplicate(clientKeyToClientData.get(ClientKey.of(client).toString()).getSubscribedTopics(), topic); - + // Client{clientIdentifier='hmq_RcrDi_18591249_30a12e2c4bbd17e322a300a4257d76bd', hostname='broker.hivemq.com'} -> {ClientData@3806} + // Client{clientIdentifier='hmq_RcrDi_18591249_30a12e2c4bbd17e322a300a4257d76bd', hostname='broker.hivemq.com'} if (!intersectingFilters.isEmpty()) { Logger.warn("WARN: New subscription to '{}' intersects with already existing subscription(s) {}", topic, intersectingFilters); } - final int qosI = i < subscribe.getQos().length ? i : subscribe.getQos().length - 1; - final MqttQos qos = subscribe.getQos()[qosI]; + final int qosI = i < subscribeOptions.getQos().length ? i : subscribeOptions.getQos().length - 1; + final MqttQos qos = subscribeOptions.getQos()[qosI]; switch (client.getConfig().getMqttVersion()) { case MQTT_5_0: - mqtt5Subscribe((Mqtt5Client) client, subscribe, topic, qos); + mqtt5Subscribe((Mqtt5Client) client, subscribeOptions, topic, qos); break; case MQTT_3_1_1: - mqtt3Subscribe((Mqtt3Client) client, subscribe, topic, qos); + mqtt3Subscribe((Mqtt3Client) client, subscribeOptions, topic, qos); break; } } @@ -169,108 +186,86 @@ public void subscribe(final @NotNull MqttClient client, final @NotNull Subscribe return intersectingFilters; } - public void publish(final @NotNull PublishCommand publishCommand) { - final MqttClient client = connect(publishCommand); - - publish(client, publishCommand); - } - - public void publish(final @NotNull MqttClient client, final @NotNull Publish publish) { - for (int i = 0; i < publish.getTopics().length; i++) { - final String topic = publish.getTopics()[i]; - final int qosI = i < publish.getQos().length ? i : publish.getQos().length - 1; - final MqttQos qos = publish.getQos()[qosI]; + public void publish(final @NotNull MqttClient client, final @NotNull PublishOptions publishOptions) { + for (int i = 0; i < publishOptions.getTopics().length; i++) { + final String topic = publishOptions.getTopics()[i]; + final int qosI = i < publishOptions.getQos().length ? i : publishOptions.getQos().length - 1; + final MqttQos qos = publishOptions.getQos()[qosI]; switch (client.getConfig().getMqttVersion()) { case MQTT_5_0: - mqtt5Publish((Mqtt5Client) client, publish, topic, qos); + mqtt5Publish((Mqtt5Client) client, publishOptions, topic, qos); break; case MQTT_3_1_1: - mqtt3Publish((Mqtt3Client) client, publish, topic, qos); + mqtt3Publish((Mqtt3Client) client, publishOptions, topic, qos); break; } } } - public void disconnect(final @NotNull Disconnect disconnect) { - final String clientKey = disconnect.getKey(); - - if (clientKeyToClientData.containsKey(clientKey)) { - final MqttClient client = clientKeyToClientData.get(clientKey).getClient(); + public void disconnect(final @NotNull ClientKey clientKey, final @NotNull DisconnectOptions disconnectOptions) { + final ClientData clientData = clientKeyToClientData.get(clientKey.toString()); + if (clientData != null) { + disconnect(clientData.getClient(), disconnectOptions); + } + } - switch (client.getConfig().getMqttVersion()) { - case MQTT_5_0: - mqtt5Disconnect((Mqtt5Client) client, disconnect); - break; - case MQTT_3_1_1: - mqtt3Disconnect((Mqtt3Client) client, disconnect); - break; - } - clientKeyToClientData.remove(clientKey); - } else { - Logger.error("client to disconnect is not connected ({}) ", clientKey); + public void disconnect(final @NotNull MqttClient client, final @NotNull DisconnectOptions disconnectOptions) { + switch (client.getConfig().getMqttVersion()) { + case MQTT_5_0: + mqtt5Disconnect((Mqtt5Client) client, disconnectOptions); + break; + case MQTT_3_1_1: + mqtt3Disconnect((Mqtt3Client) client, disconnectOptions); + break; } + clientKeyToClientData.remove(ClientKey.of(client).toString()); } - public void disconnectAllClients(final @NotNull Disconnect disconnect) { + public void disconnectAllClients(final @NotNull DisconnectOptions disconnectOptions) { for (final Map.Entry entry : clientKeyToClientData.entrySet()) { final MqttClient client = entry.getValue().getClient(); switch (client.getConfig().getMqttVersion()) { case MQTT_5_0: - mqtt5Disconnect((Mqtt5Client) client, disconnect); + mqtt5Disconnect((Mqtt5Client) client, disconnectOptions); break; case MQTT_3_1_1: - mqtt3Disconnect((Mqtt3Client) client, disconnect); + mqtt3Disconnect((Mqtt3Client) client, disconnectOptions); break; } } clientKeyToClientData.clear(); } - public void unsubscribe(final @NotNull MqttClient client, final @NotNull Unsubscribe unsubscribe) { + public void unsubscribe(final @NotNull MqttClient client, final @NotNull UnsubscribeOptions unsubscribeOptions) { switch (client.getConfig().getMqttVersion()) { case MQTT_5_0: - mqtt5Unsubscribe((Mqtt5Client) client, unsubscribe); + mqtt5Unsubscribe((Mqtt5Client) client, unsubscribeOptions); break; case MQTT_3_1_1: - mqtt3Unsubscribe((Mqtt3Client) client, unsubscribe); + mqtt3Unsubscribe((Mqtt3Client) client, unsubscribeOptions); break; } } - public boolean isConnected(final @NotNull Context context) { - if (clientKeyToClientData.containsKey(context.getKey())) { - final MqttClient client = clientKeyToClientData.get(context.getKey()).getClient(); + public boolean isConnected(final @NotNull String key) { + if (clientKeyToClientData.containsKey(key)) { + final MqttClient client = clientKeyToClientData.get(key).getClient(); final MqttClientState state = client.getState(); return state.isConnected(); } return false; } - public @NotNull MqttClient connect(final @NotNull Connect connect) { - if (isConnected(connect)) { - Logger.debug("Client is already connected ({})", connect.getKey()); - Logger.info("Using already connected ({})", connect.getKey()); - return clientKeyToClientData.get(connect.getKey()).getClient(); - } - - switch (connect.getVersion()) { - case MQTT_5_0: - return connectMqtt5Client(connect); - case MQTT_3_1_1: - return connectMqtt3Client(connect); - } - - throw new IllegalStateException( - "The MQTT Version specified is not supported. Version was " + connect.getVersion()); - } - - private @NotNull Mqtt5Client connectMqtt5Client(final @NotNull Connect connect) { - final MqttClientBuilder clientBuilder = createBuilder(connect); + private @NotNull Mqtt5Client connectMqtt5Client( + final @NotNull ConnectOptions connectOptions, final @Nullable SubscribeOptions subscribeOptions) + throws Exception { + final MqttClientBuilder clientBuilder = createBuilder(connectOptions); final Mqtt5Client client = clientBuilder.useMqttVersion5().build(); - final Mqtt5Publish willPublish = createMqtt5WillPublish(connect); - final Mqtt5ConnectRestrictions connectRestrictions = createMqtt5ConnectRestrictions(connect); + final Mqtt5Publish willPublish = createMqtt5WillPublish(connectOptions.getWillOptions()); + final Mqtt5ConnectRestrictions connectRestrictions = + createMqtt5ConnectRestrictions(connectOptions.getConnectRestrictionOptions()); final Mqtt5ConnectBuilder connectBuilder = Mqtt5Connect.builder().willPublish(willPublish); @@ -281,65 +276,69 @@ public boolean isConnected(final @NotNull Context context) { connectBuilder.restrictions(connectRestrictions); } - if (connect.getCleanStart() != null) { + if (connectOptions.getCleanStart() != null) { //noinspection ResultOfMethodCallIgnored - connectBuilder.cleanStart(connect.getCleanStart()); + connectBuilder.cleanStart(connectOptions.getCleanStart()); } - if (connect.getKeepAlive() != null) { + if (connectOptions.getKeepAlive() != null) { //noinspection ResultOfMethodCallIgnored - connectBuilder.keepAlive(connect.getKeepAlive()); + connectBuilder.keepAlive(connectOptions.getKeepAlive()); } - if (connect.getSessionExpiryInterval() != null) { + if (connectOptions.getSessionExpiryInterval() != null) { //noinspection ResultOfMethodCallIgnored - connectBuilder.sessionExpiryInterval(connect.getSessionExpiryInterval()); + connectBuilder.sessionExpiryInterval(connectOptions.getSessionExpiryInterval()); } - if (connect.getConnectUserProperties() != null) { + if (connectOptions.getConnectUserProperties() != null) { //noinspection ResultOfMethodCallIgnored - connectBuilder.userProperties(connect.getConnectUserProperties()); + connectBuilder.userProperties(connectOptions.getConnectUserProperties()); } //noinspection ResultOfMethodCallIgnored - connectBuilder.simpleAuth(buildMqtt5Authentication(connect)); + connectBuilder.simpleAuth(buildMqtt5Authentication(connectOptions.getAuthenticationOptions())); client.toAsync() - .publishes(MqttGlobalPublishFilter.REMAINING, buildRemainingMqtt5PublishesCallback(connect, client)); + .publishes(MqttGlobalPublishFilter.REMAINING, + buildRemainingMqtt5PublishesCallback(subscribeOptions, client)); + - mqtt5Connect(client, connectBuilder.build(), connect); + mqtt5Connect(client, connectBuilder.build()); final ClientData clientData = new ClientData(client); - final String key = MqttUtils.buildKey(client.getConfig().getClientIdentifier().map(Object::toString).orElse(""), - client.getConfig().getServerHost()); + final String key = ClientKey.of(client).toString(); clientKeyToClientData.put(key, clientData); return client; } - private @NotNull Mqtt3Client connectMqtt3Client(final @NotNull Connect connect) { - final MqttClientBuilder clientBuilder = createBuilder(connect); + private @NotNull Mqtt3Client connectMqtt3Client( + final @NotNull ConnectOptions connectOptions, final @Nullable SubscribeOptions subscribeOptions) + throws Exception { + final MqttClientBuilder clientBuilder = createBuilder(connectOptions); final Mqtt3Client client = clientBuilder.useMqttVersion3().build(); - final Mqtt3Publish willPublish = createMqtt3WillPublish(connect); + final Mqtt3Publish willPublish = createMqtt3WillPublish(connectOptions.getWillOptions()); final Mqtt3ConnectBuilder connectBuilder = Mqtt3Connect.builder().willPublish(willPublish); - if (connect.getCleanStart() != null) { + if (connectOptions.getCleanStart() != null) { //noinspection ResultOfMethodCallIgnored - connectBuilder.cleanSession(connect.getCleanStart()); + connectBuilder.cleanSession(connectOptions.getCleanStart()); } - if (connect.getKeepAlive() != null) { + if (connectOptions.getKeepAlive() != null) { //noinspection ResultOfMethodCallIgnored - connectBuilder.keepAlive(connect.getKeepAlive()); + connectBuilder.keepAlive(connectOptions.getKeepAlive()); } //noinspection ResultOfMethodCallIgnored - connectBuilder.simpleAuth(buildMqtt3Authentication(connect)); + connectBuilder.simpleAuth(buildMqtt3Authentication(connectOptions.getAuthenticationOptions())); client.toAsync() - .publishes(MqttGlobalPublishFilter.REMAINING, buildRemainingMqtt3PublishesCallback(connect, client)); + .publishes(MqttGlobalPublishFilter.REMAINING, + buildRemainingMqtt3PublishesCallback(subscribeOptions, client)); - mqtt3Connect(client, connectBuilder.build(), connect); + mqtt3Connect(client, connectBuilder.build()); final ClientData clientData = new ClientData(client); @@ -351,126 +350,134 @@ public boolean isConnected(final @NotNull Context context) { return client; } - private @Nullable Mqtt5Publish createMqtt5WillPublish(final @NotNull Will will) { + private @Nullable Mqtt5Publish createMqtt5WillPublish(final @NotNull WillOptions willOptions) { // only topic is mandatory for will message creation - if (will.getWillTopic() != null) { - final ByteBuffer willPayload = will.getWillMessage(); + if (willOptions.getWillTopic() != null) { + final ByteBuffer willPayload = willOptions.getWillMessage(); final Mqtt5WillPublishBuilder.Complete builder = Mqtt5WillPublish.builder() - .topic(will.getWillTopic()) + .topic(willOptions.getWillTopic()) .payload(willPayload) - .qos(Objects.requireNonNull(will.getWillQos())) - .payloadFormatIndicator(will.getWillPayloadFormatIndicator()) - .contentType(will.getWillContentType()) - .responseTopic(will.getWillResponseTopic()) - .correlationData(will.getWillCorrelationData()); + .qos(Objects.requireNonNull(willOptions.getWillQos())) + .payloadFormatIndicator(willOptions.getWillPayloadFormatIndicator()) + .contentType(willOptions.getWillContentType()) + .responseTopic(willOptions.getWillResponseTopic()) + .correlationData(willOptions.getWillCorrelationData()); - if (will.getWillRetain() != null) { + if (willOptions.getWillRetain() != null) { //noinspection ResultOfMethodCallIgnored - builder.retain(will.getWillRetain()); + builder.retain(willOptions.getWillRetain()); } - if (will.getWillMessageExpiryInterval() != null) { + if (willOptions.getWillMessageExpiryInterval() != null) { //noinspection ResultOfMethodCallIgnored - builder.messageExpiryInterval(will.getWillMessageExpiryInterval()); + builder.messageExpiryInterval(willOptions.getWillMessageExpiryInterval()); } - if (will.getWillDelayInterval() != null) { + if (willOptions.getWillDelayInterval() != null) { //noinspection ResultOfMethodCallIgnored - builder.delayInterval(will.getWillDelayInterval()); + builder.delayInterval(willOptions.getWillDelayInterval()); } - if (will.getWillUserProperties() != null) { // user Properties can't be completed with null + if (willOptions.getWillUserProperties() != null) { // user Properties can't be completed with null //noinspection ResultOfMethodCallIgnored - builder.userProperties(will.getWillUserProperties()); + builder.userProperties(willOptions.getWillUserProperties()); } return builder.build().asWill(); - } else if (will.getWillMessage() != null) { - Logger.warn("option -wt is missing if a will message is configured - command was: {} ", will.toString()); + } else if (willOptions.getWillMessage() != null) { + Logger.warn("option -wt is missing if a will message is configured - will options were: {} ", + willOptions.toString()); } return null; } - private @Nullable Mqtt3Publish createMqtt3WillPublish(final @NotNull Will will) { - if (will.getWillTopic() != null) { - final ByteBuffer willPayload = will.getWillMessage(); + private @Nullable Mqtt3Publish createMqtt3WillPublish(final @NotNull WillOptions willOptions) { + if (willOptions.getWillTopic() != null) { + final ByteBuffer willPayload = willOptions.getWillMessage(); final Mqtt3PublishBuilder.Complete builder = Mqtt3Publish.builder() - .topic(will.getWillTopic()) + .topic(willOptions.getWillTopic()) .payload(willPayload) - .qos(Objects.requireNonNull(will.getWillQos())); + .qos(Objects.requireNonNull(willOptions.getWillQos())); - if (will.getWillRetain() != null) { + if (willOptions.getWillRetain() != null) { //noinspection ResultOfMethodCallIgnored - builder.retain(will.getWillRetain()); + builder.retain(willOptions.getWillRetain()); } return builder.build(); - } else if (will.getWillMessage() != null) { - Logger.warn("option -wt is missing if a will message is configured - command was: {} ", will.toString()); + } else if (willOptions.getWillMessage() != null) { + Logger.warn("option -wt is missing if a will message is configured - will options were: {} ", + willOptions.toString()); } return null; } - private @NotNull Mqtt5ConnectRestrictions createMqtt5ConnectRestrictions(final @NotNull ConnectRestrictions connectRestrictions) { + private @NotNull Mqtt5ConnectRestrictions createMqtt5ConnectRestrictions(final @NotNull ConnectRestrictionOptions connectRestrictionOptions) { final Mqtt5ConnectRestrictionsBuilder restrictionsBuilder = Mqtt5ConnectRestrictions.builder(); - if (connectRestrictions.getReceiveMaximum() != null) { + if (connectRestrictionOptions.getReceiveMaximum() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.receiveMaximum(connectRestrictions.getReceiveMaximum()); + restrictionsBuilder.receiveMaximum(connectRestrictionOptions.getReceiveMaximum()); } - if (connectRestrictions.getSendMaximum() != null) { + if (connectRestrictionOptions.getSendMaximum() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.sendMaximum(connectRestrictions.getSendMaximum()); + restrictionsBuilder.sendMaximum(connectRestrictionOptions.getSendMaximum()); } - if (connectRestrictions.getMaximumPacketSize() != null) { + if (connectRestrictionOptions.getMaximumPacketSize() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.maximumPacketSize(connectRestrictions.getMaximumPacketSize()); + restrictionsBuilder.maximumPacketSize(connectRestrictionOptions.getMaximumPacketSize()); } - if (connectRestrictions.getSendMaximumPacketSize() != null) { + if (connectRestrictionOptions.getSendMaximumPacketSize() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.sendMaximumPacketSize(connectRestrictions.getSendMaximumPacketSize()); + restrictionsBuilder.sendMaximumPacketSize(connectRestrictionOptions.getSendMaximumPacketSize()); } - if (connectRestrictions.getTopicAliasMaximum() != null) { + if (connectRestrictionOptions.getTopicAliasMaximum() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.topicAliasMaximum(connectRestrictions.getTopicAliasMaximum()); + restrictionsBuilder.topicAliasMaximum(connectRestrictionOptions.getTopicAliasMaximum()); } - if (connectRestrictions.getSendTopicAliasMaximum() != null) { + if (connectRestrictionOptions.getSendTopicAliasMaximum() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.sendTopicAliasMaximum(connectRestrictions.getSendTopicAliasMaximum()); + restrictionsBuilder.sendTopicAliasMaximum(connectRestrictionOptions.getSendTopicAliasMaximum()); } - if (connectRestrictions.getRequestProblemInformation() != null) { + if (connectRestrictionOptions.getRequestProblemInformation() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.requestProblemInformation(connectRestrictions.getRequestProblemInformation()); + restrictionsBuilder.requestProblemInformation(connectRestrictionOptions.getRequestProblemInformation()); } - if (connectRestrictions.getRequestResponseInformation() != null) { + if (connectRestrictionOptions.getRequestResponseInformation() != null) { //noinspection ResultOfMethodCallIgnored - restrictionsBuilder.requestResponseInformation(connectRestrictions.getRequestResponseInformation()); + restrictionsBuilder.requestResponseInformation(connectRestrictionOptions.getRequestResponseInformation()); } return restrictionsBuilder.build(); } - private @NotNull MqttClientBuilder createBuilder(final @NotNull Connect connect) { + private @NotNull MqttClientBuilder createBuilder(final @NotNull ConnectOptions connectOptions) throws Exception { return MqttClient.builder() .addDisconnectedListener(new ContextClientDisconnectListener()) - .webSocketConfig(connect.getWebSocketConfig()) - .serverHost(connect.getHost()) - .serverPort(connect.getPort()) - .sslConfig(connect.getSslConfig()) - .identifier(connect.getIdentifier()); + .webSocketConfig(connectOptions.getWebSocketConfig()) + .serverHost(connectOptions.getHost()) + .serverPort(connectOptions.getPort()) + .sslConfig(connectOptions.buildSslConfig()) + .identifier(connectOptions.getIdentifier()); } - private @Nullable Mqtt5SimpleAuth buildMqtt5Authentication(final @NotNull Connect connect) { - if (connect.getUser() != null && connect.getPassword() != null) { - return Mqtt5SimpleAuth.builder().username(connect.getUser()).password(connect.getPassword()).build(); - } else if (connect.getPassword() != null) { - return Mqtt5SimpleAuth.builder().password(connect.getPassword()).build(); - } else if (connect.getUser() != null) { - return Mqtt5SimpleAuth.builder().username(connect.getUser()).build(); + private @Nullable Mqtt5SimpleAuth buildMqtt5Authentication(final @NotNull AuthenticationOptions authenticationOptions) { + if (authenticationOptions.getUser() != null && authenticationOptions.getPassword() != null) { + return Mqtt5SimpleAuth.builder() + .username(authenticationOptions.getUser()) + .password(authenticationOptions.getPassword()) + .build(); + } else if (authenticationOptions.getPassword() != null) { + return Mqtt5SimpleAuth.builder().password(authenticationOptions.getPassword()).build(); + } else if (authenticationOptions.getUser() != null) { + return Mqtt5SimpleAuth.builder().username(authenticationOptions.getUser()).build(); } return null; } - private @Nullable Mqtt3SimpleAuth buildMqtt3Authentication(final @NotNull Connect connect) { - if (connect.getUser() != null && connect.getPassword() != null) { - return Mqtt3SimpleAuth.builder().username(connect.getUser()).password(connect.getPassword()).build(); - } else if (connect.getUser() != null) { - return Mqtt3SimpleAuth.builder().username(connect.getUser()).build(); - } else if (connect.getPassword() != null) { + private @Nullable Mqtt3SimpleAuth buildMqtt3Authentication(final @NotNull AuthenticationOptions authenticationOptions) { + if (authenticationOptions.getUser() != null && authenticationOptions.getPassword() != null) { + return Mqtt3SimpleAuth.builder() + .username(authenticationOptions.getUser()) + .password(authenticationOptions.getPassword()) + .build(); + } else if (authenticationOptions.getUser() != null) { + return Mqtt3SimpleAuth.builder().username(authenticationOptions.getUser()).build(); + } else if (authenticationOptions.getPassword() != null) { throw new IllegalArgumentException("Password-Only Authentication is not allowed in MQTT 3"); } return null; @@ -480,20 +487,20 @@ public boolean isConnected(final @NotNull Context context) { return clientKeyToClientData; } - public @Nullable MqttClient getMqttClient(final @NotNull Context context) { + public @Nullable MqttClient getMqttClient(final @NotNull ClientKey clientKey) { MqttClient client = null; - if (clientKeyToClientData.containsKey(context.getKey())) { - client = clientKeyToClientData.get(context.getKey()).getClient(); + if (clientKeyToClientData.containsKey(clientKey.toString())) { + client = clientKeyToClientData.get(clientKey.toString()).getClient(); } return client; } private @NotNull Consumer buildRemainingMqtt5PublishesCallback( - final @NotNull Connect connect, final @NotNull Mqtt5Client client) { - if (connect instanceof Subscribe) { - return new SubscribeMqtt5PublishCallback((Subscribe) connect, client); + final @Nullable SubscribeOptions subscribeOptions, final @NotNull Mqtt5Client client) { + if (subscribeOptions != null) { + return new SubscribeMqtt5PublishCallback(subscribeOptions, client); } else { return mqtt5Publish -> Logger.debug("received PUBLISH: {}, MESSAGE: '{}'", mqtt5Publish, @@ -502,13 +509,14 @@ public boolean isConnected(final @NotNull Context context) { } private @NotNull Consumer buildRemainingMqtt3PublishesCallback( - final @NotNull Connect connect, final @NotNull Mqtt3Client client) { - if (connect instanceof Subscribe) { - return new SubscribeMqtt3PublishCallback((Subscribe) connect, client); + final @Nullable SubscribeOptions subscribeOptions, final @NotNull Mqtt3Client client) { + if (subscribeOptions != null) { + return new SubscribeMqtt3PublishCallback(subscribeOptions, client); } else { return mqtt3Publish -> Logger.debug("received PUBLISH: {}, MESSAGE: '{}'", mqtt3Publish, new String(mqtt3Publish.getPayloadAsBytes(), StandardCharsets.UTF_8)); } } + } diff --git a/src/main/java/com/hivemq/cli/mqtt/ClientKey.java b/src/main/java/com/hivemq/cli/mqtt/ClientKey.java new file mode 100644 index 000000000..bc7eedef1 --- /dev/null +++ b/src/main/java/com/hivemq/cli/mqtt/ClientKey.java @@ -0,0 +1,33 @@ +package com.hivemq.cli.mqtt; + +import com.hivemq.client.mqtt.MqttClient; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class ClientKey { + + private final @Nullable String clientIdentifier; + private final @NotNull String hostname; + + private ClientKey(final @Nullable String clientIdentifier, final @NotNull String hostname) { + this.clientIdentifier = clientIdentifier; + this.hostname = hostname; + } + + public static ClientKey of(final @Nullable String clientIdentifier, final @NotNull String hostname) { + return new ClientKey(clientIdentifier, hostname); + } + + public static ClientKey of(final @NotNull MqttClient client) { + return new ClientKey(client.getConfig().getClientIdentifier().map(Objects::toString).orElse(""), + client.getConfig().getServerHost()); + } + + + @Override + public String toString() { + return "Client{" + "clientIdentifier='" + clientIdentifier + '\'' + ", hostname='" + hostname + '\'' + '}'; + } +} diff --git a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java index af52e8080..086a2ac48 100644 --- a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java +++ b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java @@ -17,7 +17,7 @@ package com.hivemq.cli.mqtt; import com.google.common.base.Throwables; -import com.hivemq.cli.commands.*; +import com.hivemq.cli.commands.options.*; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.datatypes.MqttQos; @@ -57,8 +57,7 @@ public class MqttClientExecutor extends AbstractMqttClientExecutor { void mqtt5Connect( final @NotNull Mqtt5Client client, - final @NotNull Mqtt5Connect connectMessage, - final @NotNull Connect connect) { + final @NotNull Mqtt5Connect connectMessage) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); Logger.debug("{} sending CONNECT {}", clientLogPrefix, connectMessage); @@ -70,8 +69,7 @@ void mqtt5Connect( void mqtt3Connect( final @NotNull Mqtt3Client client, - final @NotNull Mqtt3Connect connectMessage, - final @NotNull Connect connect) { + final @NotNull Mqtt3Connect connectMessage) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); Logger.debug("{} sending CONNECT {}", clientLogPrefix, connectMessage); @@ -83,15 +81,15 @@ void mqtt3Connect( void mqtt5Subscribe( final @NotNull Mqtt5Client client, - final @NotNull Subscribe subscribe, + final @NotNull SubscribeOptions subscribeOptions, final @NotNull String topic, final @NotNull MqttQos qos) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); final Mqtt5SubscribeBuilder.Start.Complete builder = Mqtt5Subscribe.builder().topicFilter(topic).qos(qos); - if (subscribe.getUserProperties() != null) { + if (subscribeOptions.getUserProperties() != null) { //noinspection ResultOfMethodCallIgnored - builder.userProperties(subscribe.getUserProperties()); + builder.userProperties(subscribeOptions.getUserProperties()); } final Mqtt5Subscribe subscribeMessage = builder.build(); @@ -99,7 +97,7 @@ void mqtt5Subscribe( Logger.debug("{} sending SUBSCRIBE {}", clientLogPrefix, subscribeMessage); client.toAsync() - .subscribe(subscribeMessage, new SubscribeMqtt5PublishCallback(subscribe, client)) + .subscribe(subscribeMessage, new SubscribeMqtt5PublishCallback(subscribeOptions, client)) .whenComplete((subAck, throwable) -> { if (throwable != null) { Logger.error(throwable, @@ -108,14 +106,8 @@ void mqtt5Subscribe( topic, Throwables.getRootCause(throwable).getMessage()); } else { - final String clientKey = MqttUtils.buildKey(client.getConfig() - .getClientIdentifier() - .map(Object::toString) - .orElse(""), - client.getConfig().getServerHost()); - + final String clientKey = ClientKey.of(client).toString(); getClientDataMap().get(clientKey).addSubscription(MqttTopicFilter.of(topic)); - Logger.debug("{} received SUBACK {}", clientLogPrefix, subAck); } }) @@ -124,7 +116,7 @@ void mqtt5Subscribe( void mqtt3Subscribe( final @NotNull Mqtt3Client client, - final @NotNull Subscribe subscribe, + final @NotNull SubscribeOptions subscribeOptions, final @NotNull String topic, final @NotNull MqttQos qos) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); @@ -134,7 +126,7 @@ void mqtt3Subscribe( Logger.debug("{} sending SUBSCRIBE {}", clientLogPrefix, subscribeMessage); client.toAsync() - .subscribe(subscribeMessage, new SubscribeMqtt3PublishCallback(subscribe, client)) + .subscribe(subscribeMessage, new SubscribeMqtt3PublishCallback(subscribeOptions, client)) .whenComplete((subAck, throwable) -> { if (throwable != null) { Logger.error(throwable, @@ -159,7 +151,7 @@ void mqtt3Subscribe( void mqtt5Publish( final @NotNull Mqtt5Client client, - final @NotNull Publish publish, + final @NotNull PublishOptions publishOptions, final @NotNull String topic, final @NotNull MqttQos qos) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); @@ -167,30 +159,30 @@ void mqtt5Publish( final Mqtt5PublishBuilder.Complete publishBuilder = Mqtt5Publish.builder() .topic(topic) .qos(qos) - .payload(publish.getMessage()) - .payloadFormatIndicator(publish.getPayloadFormatIndicator()) - .contentType(publish.getContentType()) - .responseTopic(publish.getResponseTopic()) - .correlationData(publish.getCorrelationData()); + .payload(publishOptions.getMessage()) + .payloadFormatIndicator(publishOptions.getPayloadFormatIndicator()) + .contentType(publishOptions.getContentType()) + .responseTopic(publishOptions.getResponseTopic()) + .correlationData(publishOptions.getCorrelationData()); - if (publish.getRetain() != null) { + if (publishOptions.getRetain() != null) { //noinspection ResultOfMethodCallIgnored - publishBuilder.retain(publish.getRetain()); + publishBuilder.retain(publishOptions.getRetain()); } - if (publish.getMessageExpiryInterval() != null) { + if (publishOptions.getMessageExpiryInterval() != null) { //noinspection ResultOfMethodCallIgnored - publishBuilder.messageExpiryInterval(publish.getMessageExpiryInterval()); + publishBuilder.messageExpiryInterval(publishOptions.getMessageExpiryInterval()); } - if (publish.getUserProperties() != null) { + if (publishOptions.getUserProperties() != null) { //noinspection ResultOfMethodCallIgnored - publishBuilder.userProperties(publish.getUserProperties()); + publishBuilder.userProperties(publishOptions.getUserProperties()); } final Mqtt5Publish publishMessage = publishBuilder.build(); Logger.debug("{} sending PUBLISH ('{}') {}", clientLogPrefix, - bufferToString(publish.getMessage()), + bufferToString(publishOptions.getMessage()), publishMessage); client.toAsync().publish(publishMessage).whenComplete((publishResult, throwable) -> { @@ -208,24 +200,24 @@ void mqtt5Publish( void mqtt3Publish( final @NotNull Mqtt3Client client, - final @NotNull Publish publish, + final @NotNull PublishOptions publishOptions, final @NotNull String topic, final @NotNull MqttQos qos) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); final Mqtt3PublishBuilder.Complete publishBuilder = - Mqtt3Publish.builder().topic(topic).qos(qos).payload(publish.getMessage()); + Mqtt3Publish.builder().topic(topic).qos(qos).payload(publishOptions.getMessage()); - if (publish.getRetain() != null) { + if (publishOptions.getRetain() != null) { //noinspection ResultOfMethodCallIgnored - publishBuilder.retain(publish.getRetain()); + publishBuilder.retain(publishOptions.getRetain()); } final Mqtt3Publish publishMessage = publishBuilder.build(); Logger.debug("{} sending PUBLISH ('{}') {}", clientLogPrefix, - bufferToString(publish.getMessage()), + bufferToString(publishOptions.getMessage()), publishMessage); client.toAsync().publish(publishMessage).whenComplete((publishResult, throwable) -> { @@ -242,10 +234,10 @@ void mqtt3Publish( } @Override - void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull Unsubscribe unsubscribe) { + void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull UnsubscribeOptions unsubscribeOptions) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); - for (final String topic : unsubscribe.getTopics()) { + for (final String topic : unsubscribeOptions.getTopics()) { final Mqtt5Unsubscribe unsubscribeMessage = Mqtt5Unsubscribe.builder().topicFilter(topic).build(); Logger.debug("{} sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); @@ -261,7 +253,7 @@ void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull Unsubscr topic, Throwables.getRootCause(throwable).getMessage()); } else { - getClientDataMap().get(unsubscribe.getKey()).removeSubscription(MqttTopicFilter.of(topic)); + getClientDataMap().get(ClientKey.of(client).toString()).removeSubscription(MqttTopicFilter.of(topic)); Logger.debug("{} received UNSUBACK {}", clientLogPrefix, unsubAck); } @@ -271,10 +263,10 @@ void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull Unsubscr } @Override - void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull Unsubscribe unsubscribe) { + void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull UnsubscribeOptions unsubscribeOptions) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); - for (final String topic : unsubscribe.getTopics()) { + for (final String topic : unsubscribeOptions.getTopics()) { final Mqtt3Unsubscribe unsubscribeMessage = Mqtt3Unsubscribe.builder().topicFilter(topic).build(); Logger.debug("{} Sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); @@ -287,7 +279,7 @@ void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull Unsubscr topic, Throwables.getRootCause(throwable).getMessage()); } else { - getClientDataMap().get(unsubscribe.getKey()).removeSubscription(MqttTopicFilter.of(topic)); + getClientDataMap().get(ClientKey.of(client).toString()).removeSubscription(MqttTopicFilter.of(topic)); Logger.debug("{} received UNSUBACK", clientLogPrefix); } }).join(); @@ -295,21 +287,21 @@ void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull Unsubscr } @Override - void mqtt5Disconnect(final @NotNull Mqtt5Client client, final @NotNull Disconnect disconnect) { + void mqtt5Disconnect(final @NotNull Mqtt5Client client, final @NotNull DisconnectOptions disconnectOptions) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); final Mqtt5DisconnectBuilder disconnectBuilder = Mqtt5Disconnect.builder(); - if (disconnect.getReasonString() != null) { + if (disconnectOptions.getReasonString() != null) { //noinspection ResultOfMethodCallIgnored - disconnectBuilder.reasonString(disconnect.getReasonString()); + disconnectBuilder.reasonString(disconnectOptions.getReasonString()); } - if (disconnect.getSessionExpiryInterval() != null) { + if (disconnectOptions.getSessionExpiryInterval() != null) { //noinspection ResultOfMethodCallIgnored - disconnectBuilder.sessionExpiryInterval(disconnect.getSessionExpiryInterval()); + disconnectBuilder.sessionExpiryInterval(disconnectOptions.getSessionExpiryInterval()); } - if (disconnect.getUserProperties() != null) { + if (disconnectOptions.getUserProperties() != null) { //noinspection ResultOfMethodCallIgnored - disconnectBuilder.userProperties(disconnect.getUserProperties()); + disconnectBuilder.userProperties(disconnectOptions.getUserProperties()); } final Mqtt5Disconnect disconnectMessage = disconnectBuilder.build(); @@ -320,7 +312,7 @@ void mqtt5Disconnect(final @NotNull Mqtt5Client client, final @NotNull Disconnec } @Override - void mqtt3Disconnect(final @NotNull Mqtt3Client client, final @NotNull Disconnect disconnect) { + void mqtt3Disconnect(final @NotNull Mqtt3Client client, final @NotNull DisconnectOptions disconnectOptions) { Logger.debug("{} Sending DISCONNECT", LoggerUtils.getClientPrefix(client.getConfig())); client.toBlocking().disconnect(); diff --git a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java index 0cd83eb0d..814211255 100644 --- a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java +++ b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java @@ -16,7 +16,7 @@ package com.hivemq.cli.mqtt; -import com.hivemq.cli.commands.Subscribe; +import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.cli.utils.MqttPublishUtils; import com.hivemq.cli.utils.json.JsonMqttPublish; @@ -39,12 +39,12 @@ public class SubscribeMqtt3PublishCallback implements Consumer { private final boolean isJsonOutput; private final boolean showTopics; - SubscribeMqtt3PublishCallback(final @NotNull Subscribe subscribe, final @NotNull Mqtt3Client client) { - printToStdout = subscribe.isPrintToSTDOUT(); - outputFile = subscribe.getOutputFile(); - isBase64 = subscribe.isBase64(); - isJsonOutput = subscribe.isJsonOutput(); - showTopics = subscribe.showTopics(); + SubscribeMqtt3PublishCallback(final @NotNull SubscribeOptions subscribeOptions, final @NotNull Mqtt3Client client) { + printToStdout = subscribeOptions.isPrintToSTDOUT(); + outputFile = subscribeOptions.getOutputFile(); + isBase64 = subscribeOptions.isBase64(); + isJsonOutput = subscribeOptions.isJsonOutput(); + showTopics = subscribeOptions.isShowTopics(); this.client = client; } diff --git a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java index 287b33b46..912d01667 100644 --- a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java +++ b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java @@ -16,7 +16,7 @@ package com.hivemq.cli.mqtt; -import com.hivemq.cli.commands.Subscribe; +import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.cli.utils.MqttPublishUtils; import com.hivemq.cli.utils.json.JsonMqttPublish; @@ -39,12 +39,12 @@ public class SubscribeMqtt5PublishCallback implements Consumer { private final boolean isJsonOutput; private final boolean showTopics; - SubscribeMqtt5PublishCallback(final @NotNull Subscribe subscribe, final @NotNull Mqtt5Client client) { - printToStdout = subscribe.isPrintToSTDOUT(); - outputFile = subscribe.getOutputFile(); - isBase64 = subscribe.isBase64(); - isJsonOutput = subscribe.isJsonOutput(); - showTopics = subscribe.showTopics(); + SubscribeMqtt5PublishCallback(final @NotNull SubscribeOptions subscribeOptions, final @NotNull Mqtt5Client client) { + printToStdout = subscribeOptions.isPrintToSTDOUT(); + outputFile = subscribeOptions.getOutputFile(); + isBase64 = subscribeOptions.isBase64(); + isJsonOutput = subscribeOptions.isJsonOutput(); + showTopics = subscribeOptions.isShowTopics(); this.client = client; } diff --git a/src/main/java/com/hivemq/cli/utils/MqttUtils.java b/src/main/java/com/hivemq/cli/utils/MqttUtils.java index 262cada0b..3a3a74bfa 100644 --- a/src/main/java/com/hivemq/cli/utils/MqttUtils.java +++ b/src/main/java/com/hivemq/cli/utils/MqttUtils.java @@ -44,7 +44,7 @@ public enum IdentifierWarning { // The returned qos array will be filled with the default value represented by the first element in the given qos array // if the sizes do not match up. Else this method throws an IllegalArgument exception public static @NotNull MqttQos @NotNull [] arrangeQosToMatchTopics( - final @NotNull String @NotNull [] topics, final @NotNull MqttQos @NotNull [] qos) { + final @NotNull String @NotNull [] topics, final @NotNull MqttQos @NotNull [] qos) throws IllegalArgumentException { if (topics.length != qos.length && qos.length == 1) { final MqttQos defaultQos = qos[0]; final MqttQos[] newQos = new MqttQos[topics.length]; diff --git a/src/test/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutorTest.java b/src/test/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutorTest.java index 7cbdddaea..5c56850ae 100644 --- a/src/test/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutorTest.java +++ b/src/test/java/com/hivemq/cli/mqtt/AbstractMqttClientExecutorTest.java @@ -16,8 +16,7 @@ package com.hivemq.cli.mqtt; -import com.hivemq.cli.commands.*; -import com.hivemq.cli.utils.Tuple; +import com.hivemq.cli.commands.options.*; import com.hivemq.client.mqtt.MqttVersion; import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.datatypes.MqttSharedTopicFilter; @@ -48,22 +47,30 @@ class AbstractMqttClientExecutorTest { private final @NotNull MqttClientExecutor mqttClientExecutor = new MqttClientExecutor(); - private @NotNull Connect connect; + private @NotNull ConnectOptions connectOptions; + private @NotNull ConnectRestrictionOptions connectRestrictionOptions; + private @NotNull AuthenticationOptions authenticationOptions; + private @NotNull WillOptions willOptions; @BeforeEach void setUp() { - connect = mock(Connect.class); - when(connect.getKey()).thenReturn("0"); - when(connect.getHost()).thenReturn("localhost"); - when(connect.getIdentifier()).thenReturn("client"); - when(connect.getReceiveMaximum()).thenReturn(null); - when(connect.getSendMaximum()).thenReturn(null); - when(connect.getMaximumPacketSize()).thenReturn(null); - when(connect.getSendMaximumPacketSize()).thenReturn(null); - when(connect.getTopicAliasMaximum()).thenReturn(null); - when(connect.getSendTopicAliasMaximum()).thenReturn(null); - when(connect.getRequestProblemInformation()).thenReturn(null); - when(connect.getRequestResponseInformation()).thenReturn(null); + connectOptions = mock(ConnectOptions.class); + connectRestrictionOptions = mock(ConnectRestrictionOptions.class); + authenticationOptions = mock(AuthenticationOptions.class); + willOptions = mock(WillOptions.class); + when(connectOptions.getConnectRestrictionOptions()).thenReturn(connectRestrictionOptions); + when(connectOptions.getAuthenticationOptions()).thenReturn(authenticationOptions); + when(connectOptions.getWillOptions()).thenReturn(willOptions); + when(connectOptions.getHost()).thenReturn("localhost"); + when(connectOptions.getIdentifier()).thenReturn("client"); + when(connectRestrictionOptions.getReceiveMaximum()).thenReturn(null); + when(connectRestrictionOptions.getSendMaximum()).thenReturn(null); + when(connectRestrictionOptions.getMaximumPacketSize()).thenReturn(null); + when(connectRestrictionOptions.getSendMaximumPacketSize()).thenReturn(null); + when(connectRestrictionOptions.getTopicAliasMaximum()).thenReturn(null); + when(connectRestrictionOptions.getSendTopicAliasMaximum()).thenReturn(null); + when(connectRestrictionOptions.getRequestProblemInformation()).thenReturn(null); + when(connectRestrictionOptions.getRequestResponseInformation()).thenReturn(null); } @Test @@ -187,27 +194,27 @@ void checkForSharedTopicDuplicate_multipleIntersectingNormalExisting_SharedNew() } @Test - void simpleAuth_whenNoAuthIsConfigured_thenNoAuthIsSet_Mqtt5() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_5_0); + void simpleAuth_whenNoAuthIsConfigured_thenNoAuthIsSet_Mqtt5() throws Exception { + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_5_0); - final Mqtt5Client mqtt5Client = (Mqtt5Client) mqttClientExecutor.connect(connect); + final Mqtt5Client mqtt5Client = (Mqtt5Client) mqttClientExecutor.connect(connectOptions); assertFalse(mqtt5Client.getConfig().getSimpleAuth().isPresent()); } @Test - void simpleAuth_whenNoAuthIsConfigured_thenNoAuthIsSet_Mqtt3() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); + void simpleAuth_whenNoAuthIsConfigured_thenNoAuthIsSet_Mqtt3() throws Exception { + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); - final Mqtt3Client mqtt5Client = (Mqtt3Client) mqttClientExecutor.connect(connect); + final Mqtt3Client mqtt5Client = (Mqtt3Client) mqttClientExecutor.connect(connectOptions); assertFalse(mqtt5Client.getConfig().getSimpleAuth().isPresent()); } @Test - void simpleAuth_whenUsernameIsConfigured_setUsername_Mqtt5() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_5_0); - when(connect.getUser()).thenReturn("Test"); + void simpleAuth_whenUsernameIsConfigured_setUsername_Mqtt5() throws Exception { + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_5_0); + when(authenticationOptions.getUser()).thenReturn("Test"); - mqttClientExecutor.connect(connect); + mqttClientExecutor.connect(connectOptions); assertNotNull(mqttClientExecutor.getMqtt5ConnectMessage()); final Optional simpleAuth = mqttClientExecutor.getMqtt5ConnectMessage().getSimpleAuth(); @@ -217,11 +224,11 @@ void simpleAuth_whenUsernameIsConfigured_setUsername_Mqtt5() { } @Test - void simpleAuth_whenUsernameIsConfigured_setUsername_Mqtt3() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); - when(connect.getUser()).thenReturn("Test"); + void simpleAuth_whenUsernameIsConfigured_setUsername_Mqtt3() throws Exception { + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); + when(authenticationOptions.getUser()).thenReturn("Test"); - mqttClientExecutor.connect(connect); + mqttClientExecutor.connect(connectOptions); assertNotNull(mqttClientExecutor.getMqtt3ConnectMessage()); final Optional simpleAuth = mqttClientExecutor.getMqtt3ConnectMessage().getSimpleAuth(); @@ -230,11 +237,11 @@ void simpleAuth_whenUsernameIsConfigured_setUsername_Mqtt3() { } @Test - void simpleAuth_whenPasswordIsConfigured_setPassword_Mqtt5() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_5_0); - when(connect.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); + void simpleAuth_whenPasswordIsConfigured_setPassword_Mqtt5() throws Exception { + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_5_0); + when(authenticationOptions.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); - mqttClientExecutor.connect(connect); + mqttClientExecutor.connect(connectOptions); assertNotNull(mqttClientExecutor.getMqtt5ConnectMessage()); final Optional simpleAuth = mqttClientExecutor.getMqtt5ConnectMessage().getSimpleAuth(); @@ -245,18 +252,18 @@ void simpleAuth_whenPasswordIsConfigured_setPassword_Mqtt5() { @Test void simpleAuth_whenPasswordIsConfigured_setPassword_Mqtt3() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); - when(connect.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); - assertThrows(IllegalArgumentException.class, () -> mqttClientExecutor.connect(connect)); + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); + when(authenticationOptions.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); + assertThrows(IllegalArgumentException.class, () -> mqttClientExecutor.connect(connectOptions)); } @Test - void simpleAuth_whenUserNameAndPasswordIsConfigured_setUsernameAndPassword_Mqtt5() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_5_0); - when(connect.getUser()).thenReturn("Test"); - when(connect.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); + void simpleAuth_whenUserNameAndPasswordIsConfigured_setUsernameAndPassword_Mqtt5() throws Exception { + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_5_0); + when(authenticationOptions.getUser()).thenReturn("Test"); + when(authenticationOptions.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); - mqttClientExecutor.connect(connect); + mqttClientExecutor.connect(connectOptions); assertNotNull(mqttClientExecutor.getMqtt5ConnectMessage()); final Optional simpleAuth = mqttClientExecutor.getMqtt5ConnectMessage().getSimpleAuth(); @@ -268,12 +275,12 @@ void simpleAuth_whenUserNameAndPasswordIsConfigured_setUsernameAndPassword_Mqtt5 } @Test - void simpleAuth_whenUserNameAndPasswordIsConfigured_setUsernameAndPassword_Mqtt3() { - when(connect.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); - when(connect.getUser()).thenReturn("Test"); - when(connect.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); + void simpleAuth_whenUserNameAndPasswordIsConfigured_setUsernameAndPassword_Mqtt3() throws Exception { + when(connectOptions.getVersion()).thenReturn(MqttVersion.MQTT_3_1_1); + when(authenticationOptions.getUser()).thenReturn("Test"); + when(authenticationOptions.getPassword()).thenReturn(ByteBuffer.wrap("Test".getBytes(StandardCharsets.UTF_8))); - mqttClientExecutor.connect(connect); + mqttClientExecutor.connect(connectOptions); assertNotNull(mqttClientExecutor.getMqtt3ConnectMessage()); final Optional simpleAuth = mqttClientExecutor.getMqtt3ConnectMessage().getSimpleAuth(); @@ -291,62 +298,60 @@ static class MqttClientExecutor extends AbstractMqttClientExecutor { @Override void mqtt5Connect( final @NotNull Mqtt5Client client, - final @NotNull Mqtt5Connect connectMessage, - final @NotNull Connect connect) { + final @NotNull Mqtt5Connect connectMessage) { this.mqtt5ConnectMessage = connectMessage; } @Override void mqtt3Connect( final @NotNull Mqtt3Client client, - final @NotNull Mqtt3Connect connectMessage, - final @NotNull Connect connect) { + final @NotNull Mqtt3Connect connectMessage) { this.mqtt3ConnectMessage = connectMessage; } @Override void mqtt5Subscribe( final @NotNull Mqtt5Client client, - final @NotNull Subscribe subscribe, + final @NotNull SubscribeOptions subscribeOptions, final @NotNull String topic, final @NotNull MqttQos qos) {} @Override void mqtt3Subscribe( final @NotNull Mqtt3Client client, - final @NotNull Subscribe subscribe, + final @NotNull SubscribeOptions subscribeOptions, final @NotNull String topic, final @NotNull MqttQos qos) {} @Override void mqtt5Publish( final @NotNull Mqtt5Client client, - final @NotNull Publish publish, + final @NotNull PublishOptions publishOptions, final @NotNull String topic, final @NotNull MqttQos qos) {} @Override void mqtt3Publish( final @NotNull Mqtt3Client client, - final @NotNull Publish publish, + final @NotNull PublishOptions publishOptions, final @NotNull String topic, final @NotNull MqttQos qos) {} @Override void mqtt5Unsubscribe( - final @NotNull Mqtt5Client client, final @NotNull Unsubscribe unsubscribe) {} + final @NotNull Mqtt5Client client, final @NotNull UnsubscribeOptions unsubscribeOptions) {} @Override void mqtt3Unsubscribe( - final @NotNull Mqtt3Client client, final @NotNull Unsubscribe unsubscribe) {} + final @NotNull Mqtt3Client client, final @NotNull UnsubscribeOptions unsubscribeOptions) {} @Override void mqtt5Disconnect( - final @NotNull Mqtt5Client client, final @NotNull Disconnect disconnect) {} + final @NotNull Mqtt5Client client, final @NotNull DisconnectOptions disconnectOptions) {} @Override void mqtt3Disconnect( - final @NotNull Mqtt3Client client, final @NotNull Disconnect disconnect) {} + final @NotNull Mqtt3Client client, final @NotNull DisconnectOptions disconnectOptions) {} public @Nullable Mqtt5Connect getMqtt5ConnectMessage() { return mqtt5ConnectMessage; From 3a41809449d4467c64615330cb4dbe07bb9d5d68 Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 21 Jul 2022 12:13:21 +0200 Subject: [PATCH 02/64] MQTT Commands > Unsubscribe > Fix not using user properties --- .../commands/options/UnsubscribeOptions.java | 13 +++-- .../hivemq/cli/mqtt/MqttClientExecutor.java | 52 ++++++++++++------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java index 56c62c516..c66013bc1 100644 --- a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java @@ -2,6 +2,7 @@ import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; import com.hivemq.client.mqtt.MqttVersion; +import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperties; import com.hivemq.client.mqtt.mqtt5.datatypes.Mqtt5UserProperty; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,14 +27,18 @@ public UnsubscribeOptions(final @NotNull String @NotNull [] topics, final @Nulla @SuppressWarnings("unused") @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, description = "A user property for the unsubscribe message") - private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + private @NotNull Mqtt5UserProperty @Nullable [] userProperties; - public @Nullable String[] getTopics() { + public @NotNull String[] getTopics() { return topics; } - public @Nullable Mqtt5UserProperty[] getUserProperties() { - return userProperties; + public @NotNull Mqtt5UserProperties getUserProperties() { + if (userProperties != null && userProperties.length > 0) { + return Mqtt5UserProperties.of(userProperties); + } else { + return Mqtt5UserProperties.of(); + } } public static @NotNull UnsubscribeOptions of(final @NotNull SubscribeOptions subscribeOptions) { diff --git a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java index 086a2ac48..638aa9f51 100644 --- a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java +++ b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java @@ -17,7 +17,10 @@ package com.hivemq.cli.mqtt; import com.google.common.base.Throwables; -import com.hivemq.cli.commands.options.*; +import com.hivemq.cli.commands.options.DisconnectOptions; +import com.hivemq.cli.commands.options.PublishOptions; +import com.hivemq.cli.commands.options.SubscribeOptions; +import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.datatypes.MqttQos; @@ -56,8 +59,7 @@ public class MqttClientExecutor extends AbstractMqttClientExecutor { MqttClientExecutor() {} void mqtt5Connect( - final @NotNull Mqtt5Client client, - final @NotNull Mqtt5Connect connectMessage) { + final @NotNull Mqtt5Client client, final @NotNull Mqtt5Connect connectMessage) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); Logger.debug("{} sending CONNECT {}", clientLogPrefix, connectMessage); @@ -68,8 +70,7 @@ void mqtt5Connect( } void mqtt3Connect( - final @NotNull Mqtt3Client client, - final @NotNull Mqtt3Connect connectMessage) { + final @NotNull Mqtt3Client client, final @NotNull Mqtt3Connect connectMessage) { final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); Logger.debug("{} sending CONNECT {}", clientLogPrefix, connectMessage); @@ -100,7 +101,8 @@ void mqtt5Subscribe( .subscribe(subscribeMessage, new SubscribeMqtt5PublishCallback(subscribeOptions, client)) .whenComplete((subAck, throwable) -> { if (throwable != null) { - Logger.error(throwable, + Logger.error( + throwable, "{} failed SUBSCRIBE to TOPIC '{}': {}", clientLogPrefix, topic, @@ -129,16 +131,15 @@ void mqtt3Subscribe( .subscribe(subscribeMessage, new SubscribeMqtt3PublishCallback(subscribeOptions, client)) .whenComplete((subAck, throwable) -> { if (throwable != null) { - Logger.error(throwable, + Logger.error( + throwable, "{} failed SUBSCRIBE to TOPIC '{}': {}", clientLogPrefix, topic, Throwables.getRootCause(throwable).getMessage()); } else { - final String clientKey = MqttUtils.buildKey(client.getConfig() - .getClientIdentifier() - .map(Object::toString) - .orElse(""), + final String clientKey = MqttUtils.buildKey( + client.getConfig().getClientIdentifier().map(Object::toString).orElse(""), client.getConfig().getServerHost()); getClientDataMap().get(clientKey).addSubscription(MqttTopicFilter.of(topic)); @@ -180,14 +181,16 @@ void mqtt5Publish( final Mqtt5Publish publishMessage = publishBuilder.build(); - Logger.debug("{} sending PUBLISH ('{}') {}", + Logger.debug( + "{} sending PUBLISH ('{}') {}", clientLogPrefix, bufferToString(publishOptions.getMessage()), publishMessage); client.toAsync().publish(publishMessage).whenComplete((publishResult, throwable) -> { if (throwable != null) { - Logger.error(throwable, + Logger.error( + throwable, "{} failed PUBLISH to TOPIC '{}': {}", clientLogPrefix, topic, @@ -215,14 +218,16 @@ void mqtt3Publish( final Mqtt3Publish publishMessage = publishBuilder.build(); - Logger.debug("{} sending PUBLISH ('{}') {}", + Logger.debug( + "{} sending PUBLISH ('{}') {}", clientLogPrefix, bufferToString(publishOptions.getMessage()), publishMessage); client.toAsync().publish(publishMessage).whenComplete((publishResult, throwable) -> { if (throwable != null) { - Logger.error(throwable, + Logger.error( + throwable, "{} failed PUBLISH to TOPIC '{}': {}", clientLogPrefix, topic, @@ -238,7 +243,10 @@ void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull Unsubscr final String clientLogPrefix = LoggerUtils.getClientPrefix(client.getConfig()); for (final String topic : unsubscribeOptions.getTopics()) { - final Mqtt5Unsubscribe unsubscribeMessage = Mqtt5Unsubscribe.builder().topicFilter(topic).build(); + final Mqtt5Unsubscribe unsubscribeMessage = Mqtt5Unsubscribe.builder() + .topicFilter(topic) + .userProperties(unsubscribeOptions.getUserProperties()) + .build(); Logger.debug("{} sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); @@ -247,13 +255,15 @@ void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull Unsubscr .whenComplete((Mqtt5UnsubAck unsubAck, Throwable throwable) -> { if (throwable != null) { - Logger.error(throwable, + Logger.error( + throwable, "{} failed UNSUBSCRIBE from TOPIC '{}': {}", clientLogPrefix, topic, Throwables.getRootCause(throwable).getMessage()); } else { - getClientDataMap().get(ClientKey.of(client).toString()).removeSubscription(MqttTopicFilter.of(topic)); + getClientDataMap().get(ClientKey.of(client).toString()) + .removeSubscription(MqttTopicFilter.of(topic)); Logger.debug("{} received UNSUBACK {}", clientLogPrefix, unsubAck); } @@ -273,13 +283,15 @@ void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull Unsubscr client.toAsync().unsubscribe(unsubscribeMessage).whenComplete((Void unsubAck, Throwable throwable) -> { if (throwable != null) { - Logger.error(throwable, + Logger.error( + throwable, "{} failed UNSUBSCRIBE from TOPIC '{}': {}", clientLogPrefix, topic, Throwables.getRootCause(throwable).getMessage()); } else { - getClientDataMap().get(ClientKey.of(client).toString()).removeSubscription(MqttTopicFilter.of(topic)); + getClientDataMap().get(ClientKey.of(client).toString()) + .removeSubscription(MqttTopicFilter.of(topic)); Logger.debug("{} received UNSUBACK", clientLogPrefix); } }).join(); From 36aacded12fac328c7843d7fd9992e9ec2a2b1e3 Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 21 Jul 2022 12:13:52 +0200 Subject: [PATCH 03/64] MQTT Commands > Unify toString() method --- .../cli/commands/cli/SubscribeCommand.java | 6 +++++ .../options/AuthenticationOptions.java | 2 +- .../cli/commands/options/ConnectOptions.java | 22 +++++++------------ .../options/ConnectRestrictionOptions.java | 16 ++++++-------- .../cli/commands/options/PublishOptions.java | 19 ++++++---------- .../commands/options/SubscribeOptions.java | 9 ++++++++ .../cli/commands/options/WillOptions.java | 22 ++++++------------- .../commands/shell/ClearScreenCommand.java | 4 ++-- .../shell/ContextDisconnectCommand.java | 6 ++--- .../commands/shell/ContextExitCommand.java | 4 ++-- .../commands/shell/ContextPublishCommand.java | 3 +-- .../shell/ContextSubscribeCommand.java | 4 ++-- .../commands/shell/ContextSwitchCommand.java | 6 ++--- .../shell/ContextUnsubscribeCommand.java | 6 +++++ .../commands/shell/ListClientsCommand.java | 13 ++++++----- .../cli/commands/shell/ShellCommand.java | 15 +------------ .../commands/shell/ShellConnectCommand.java | 6 ++--- .../commands/shell/ShellContextCommand.java | 4 ++++ .../shell/ShellDisconnectCommand.java | 4 ++-- .../cli/commands/shell/ShellExitCommand.java | 5 ++--- .../cli/commands/shell/VersionCommand.java | 5 +++++ 21 files changed, 88 insertions(+), 93 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java index e6634aad2..366e0e133 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java @@ -125,4 +125,10 @@ private void stay() throws InterruptedException { } } + @Override + public String toString() { + return "SubscribeCommand{" + "versionInfoRequested=" + versionInfoRequested + ", usageHelpRequested=" + + usageHelpRequested + ", logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + + ", subscribeOptions=" + subscribeOptions + ", debugOptions=" + debugOptions + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java index 4b8fa70d6..91c25dec1 100644 --- a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java @@ -58,7 +58,7 @@ private void setPasswordFromFile(final @NotNull ByteBuffer passwordFromFile) { public @Nullable ByteBuffer getPassword() {return password;} @Override - public @NotNull String toString() { + public String toString() { return "AuthenticationOptions{" + "user='" + user + '\'' + ", password=" + password + '}'; } diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java index f44c5caba..6d17222aa 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java @@ -123,20 +123,14 @@ public int getPort() { } @Override - public @NotNull String toString() { - return "(host=" + host - + ", port=" + port - + ", version=" + version + - (identifier != null && !identifier.isEmpty() ? (", identifier=" + identifier) : "") + - (keepAlive != null ? (", keepAlive=" + keepAlive) : "") + - (cleanStart != null ? (", cleanStart=" + cleanStart) : "") + - (sessionExpiryInterval != null ? (", sessionExpiryInterval=" + sessionExpiryInterval) : "") + - (connectUserProperties != null ? (", userProperties=" + Arrays.toString(connectUserProperties)) : "") + - ", sslOptions=" + sslOptions + - ", useWebSocket=" + useWebSocket + - (webSocketPath != null ? (", webSocketPath=" + webSocketPath) : "") + - connectRestrictionOptions + - willOptions; + public String toString() { + return "ConnectOptions{" + "version=" + version + ", host='" + host + '\'' + ", port=" + port + + ", identifier='" + identifier + '\'' + ", identifierPrefix='" + identifierPrefix + '\'' + + ", keepAlive=" + keepAlive + ", cleanStart=" + cleanStart + ", sessionExpiryInterval=" + + sessionExpiryInterval + ", connectUserProperties=" + Arrays.toString(connectUserProperties) + + ", willOptions=" + willOptions + ", connectRestrictionOptions=" + connectRestrictionOptions + + ", authenticationOptions=" + authenticationOptions + ", sslOptions=" + sslOptions + ", useWebSocket=" + + useWebSocket + ", webSocketPath='" + webSocketPath + '\'' + '}'; } public @Nullable Integer getKeepAlive() { diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java index 029937dd4..20dbf894e 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java @@ -126,14 +126,12 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { } } - public @NotNull String toString() { - return (receiveMaximum != null ? (", receiveMaximum=" + receiveMaximum) : "") + - (sendMaximum != null ? (", sendMaximum=" + sendMaximum) : "") + - (maximumPacketSize != null ? (", maximumPacketSize=" + maximumPacketSize) : "") + - (sendMaximumPacketSize != null ? (", sendMaximumPacketSize=" + sendMaximumPacketSize) : "") + - (topicAliasMaximum != null ? (", topicAliasMaximum=" + topicAliasMaximum) : "") + - (sendTopicAliasMaximum != null ? (", sendTopicAliasMaximum=" + sendTopicAliasMaximum) : "") + - (requestProblemInformation != null ? (", requestProblemInformation=" + requestProblemInformation) : "") + - (requestResponseInformation != null ? (", requestResponseInformation=" + requestResponseInformation) : ""); + @Override + public String toString() { + return "ConnectRestrictionOptions{" + "receiveMaximum=" + receiveMaximum + ", sendMaximum=" + sendMaximum + + ", maximumPacketSize=" + maximumPacketSize + ", sendMaximumPacketSize=" + sendMaximumPacketSize + + ", topicAliasMaximum=" + topicAliasMaximum + ", sendTopicAliasMaximum=" + sendTopicAliasMaximum + + ", requestProblemInformation=" + requestProblemInformation + ", requestResponseInformation=" + + requestResponseInformation + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java index b7cef3a42..cad10fbdb 100644 --- a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java @@ -137,18 +137,13 @@ public void arrangeQosToMatchTopics(){ qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); } - public @NotNull String toString() { - return getClass().getSimpleName() + "{topics=" + Arrays.toString(topics) + - ", qos=" + - Arrays.toString(qos) + ", message=" + - new String(message.getMessageBuffer().array(), StandardCharsets.UTF_8) + - (retain != null ? (", retain=" + retain) : "") + - (messageExpiryInterval != null ? (", messageExpiryInterval=" + messageExpiryInterval) : "") + - (payloadFormatIndicator != null ? (", payloadFormatIndicator=" + payloadFormatIndicator) : "") + - (contentType != null ? (", contentType=" + contentType) : "") + - (responseTopic != null ? (", responseTopic=" + responseTopic) : "") + (correlationData != null ? - (", correlationData=" + new String(correlationData.array(), StandardCharsets.UTF_8)) : "") + - (userProperties != null ? (", userProperties=" + getUserProperties()) : "") + '}'; + @Override + public String toString() { + return "PublishOptions{" + "topics=" + Arrays.toString(topics) + ", qos=" + Arrays.toString(qos) + + ", message=" + message + ", retain=" + retain + ", messageExpiryInterval=" + messageExpiryInterval + + ", payloadFormatIndicator=" + payloadFormatIndicator + ", contentType='" + contentType + '\'' + + ", responseTopic='" + responseTopic + '\'' + ", correlationData=" + correlationData + + ", userProperties=" + Arrays.toString(userProperties) + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java index 890110fd1..c8437c340 100644 --- a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java @@ -16,6 +16,7 @@ import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Objects; public class SubscribeOptions { @@ -142,4 +143,12 @@ public void setDefaultOptions() { outputFile = new File(defaultCLIProperties.getClientSubscribeOutputFile()); } } + + @Override + public String toString() { + return "SubscribeOptions{" + "topics=" + Arrays.toString(topics) + ", qos=" + Arrays.toString(qos) + + ", userProperties=" + Arrays.toString(userProperties) + ", outputFile=" + outputFile + + ", printToSTDOUT=" + printToSTDOUT + ", base64=" + base64 + ", jsonOutput=" + jsonOutput + + ", showTopics=" + showTopics + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java index 004eeee63..e980f845e 100644 --- a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java @@ -146,20 +146,12 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { } @Override - public @NotNull String toString() { - if (willTopic == null) { - return ""; - } else { - return ", willTopic=" + willTopic + (willQos != null ? (", willQos=" + willQos) : "") + - (willMessage != null ? (", willMessage=" + willMessage) : "") + - (willRetain != null ? (", willRetain=" + willRetain) : "") + - (willMessageExpiryInterval != null ? (", willMessageExpiryInterval=" + willMessageExpiryInterval) : "") + - (willDelayInterval != null ? (", willDelayInterval=" + willDelayInterval) : "") + - (willPayloadFormatIndicator != null ? (", willPayloadFormatIndicator=" + willPayloadFormatIndicator) : "") + - (willContentType != null ? (", willContentType=" + willContentType) : "") + - (willResponseTopic != null ? (", willResponseTopic=" + willResponseTopic) : "") + - (willCorrelationData != null ? (", willCorrelationData=" + willCorrelationData) : "") + - (willUserProperties != null ? (", willUserProperties=" + Arrays.toString(willUserProperties)) : ""); - } + public String toString() { + return "WillOptions{" + "willTopic='" + willTopic + '\'' + ", willMessage=" + willMessage + ", willQos=" + + willQos + ", willRetain=" + willRetain + ", willMessageExpiryInterval=" + willMessageExpiryInterval + + ", willDelayInterval=" + willDelayInterval + ", willPayloadFormatIndicator=" + + willPayloadFormatIndicator + ", willContentType='" + willContentType + '\'' + ", willResponseTopic='" + + willResponseTopic + '\'' + ", willCorrelationData=" + willCorrelationData + ", willUserProperties=" + + Arrays.toString(willUserProperties) + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java index 58a8493d6..cf1b52d10 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java @@ -42,7 +42,7 @@ public Integer call() { } @Override - public @NotNull String toString() { - return this.getClass().getSimpleName(); + public String toString() { + return "ClearScreenCommand{" + "usageHelpRequested=" + usageHelpRequested + '}'; } } \ No newline at end of file diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java index dfe9118c9..a49f063ac 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java @@ -76,8 +76,8 @@ public Integer call() { } @Override - public @NotNull String toString() { - return getClass().getSimpleName() + disconnectOptions; + public String toString() { + return "ContextDisconnectCommand{" + "usageHelpRequested=" + usageHelpRequested + ", disconnectOptions=" + + disconnectOptions + '}'; } - } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java index d675f37c5..6440dd2da 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java @@ -48,7 +48,7 @@ public Integer call() { } @Override - public @NotNull String toString() { - return getClass().getSimpleName(); + public String toString() { + return "ContextExitCommand{" + "usageHelpRequested=" + usageHelpRequested + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java index 6f42d4752..01458eb1d 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java @@ -69,8 +69,7 @@ public Integer call() { @Override public String toString() { - return "ContextPublishCommand{" + "publishOptions=" + + return "ContextPublishCommand{" + "usageHelpRequested=" + usageHelpRequested + ", publishOptions=" + publishOptions + '}'; } - } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java index 58676f8a6..dc154189f 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java @@ -140,7 +140,7 @@ private void stay() throws InterruptedException { @Override public String toString() { - return "ContextSubscribeCommand{" + "subscribeOptions=" + subscribeOptions + ", stay=" + stay + '}'; + return "ContextSubscribeCommand{" + "subscribeOptions=" + subscribeOptions + ", usageHelpRequested=" + + usageHelpRequested + ", stay=" + stay + '}'; } - } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java index ac1ae8c2b..540915f00 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java @@ -103,8 +103,8 @@ private void extractKeyFromContextName(final String contextName) { } @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "identifier=" + identifier + ", host=" + host + '}'; + public String toString() { + return "ContextSwitchCommand{" + "usageHelpRequested=" + usageHelpRequested + ", contextName='" + contextName + + '\'' + ", identifier='" + identifier + '\'' + ", host='" + host + '\'' + '}'; } - } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java index 717a78d3f..f1e61fc9b 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java @@ -70,4 +70,10 @@ public Integer call() { return 0; } + + @Override + public String toString() { + return "ContextUnsubscribeCommand{" + "usageHelpRequested=" + usageHelpRequested + ", unsubscribeOptions=" + + unsubscribeOptions + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java index da50c3bfd..c3bf3e411 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java @@ -153,12 +153,6 @@ public Integer call() { return 0; } - @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "sortByTime=" + sortByTime + ", doNotSort=" + doNotSort + - ", reverse=" + reverse + ", listSubscriptions" + listSubscriptions + ", longOutput=" + longOutput + '}'; - } - public @NotNull List getSortedClientData() { final List sortedClientData = new ArrayList<>(MqttClientExecutor.getClientDataMap().values()); @@ -183,4 +177,11 @@ public Integer call() { sortedClientData.sort(comparator); return sortedClientData; } + + @Override + public String toString() { + return "ListClientsCommand{" + "usageHelpRequested=" + usageHelpRequested + ", sortByTime=" + sortByTime + + ", doNotSort=" + doNotSort + ", reverse=" + reverse + ", longOutput=" + longOutput + + ", listSubscriptions=" + listSubscriptions + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java index f0e353919..6fca7c1f5 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java @@ -52,10 +52,6 @@ public class ShellCommand implements Runnable { private static final @NotNull String DEFAULT_PROMPT = "mqtt> "; private static @NotNull String prompt = DEFAULT_PROMPT; - //TODO: This is never set - public static boolean DEBUG; - public static boolean VERBOSE; - public static @Nullable PrintWriter TERMINAL_WRITER; private static @Nullable LineReaderImpl currentReader; @@ -204,17 +200,8 @@ static void clearScreen() { Objects.requireNonNull(currentReader).clearScreen(); } - static boolean isVerbose() { - return VERBOSE; - } - - static boolean isDebug() { - return DEBUG; - } - @Override public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "logfilePath=" + logfilePath + ", debug=" + DEBUG + ", verbose=" + - VERBOSE + "}"; + return getClass().getSimpleName() + "{" + "logfilePath=" + logfilePath + "}"; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index 7faca16ad..1be45daa5 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -77,8 +77,8 @@ public Integer call() { } @Override - public @NotNull String toString() { - return getClass().getSimpleName() + "{" + connectOptions + "}"; + public String toString() { + return "ShellConnectCommand{" + "usageHelpRequested=" + usageHelpRequested + ", connectOptions=" + + connectOptions + '}'; } - } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java index 3839c8705..128806231 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java @@ -66,4 +66,8 @@ public Integer call() { return 0; } + @Override + public String toString() { + return "ShellContextCommand{}"; + } } \ No newline at end of file diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java index 057665032..71ab27290 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java @@ -83,7 +83,7 @@ public Integer call() { @Override public String toString() { - return "ShellDisconnectCommand{" + "disconnectOptions=" + disconnectOptions + '}'; + return "ShellDisconnectCommand{" + "usageHelpRequested=" + usageHelpRequested + ", disconnectOptions=" + + disconnectOptions + '}'; } - } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java index d93d3d6dd..001c2f2cb 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java @@ -40,9 +40,8 @@ public Integer call() { } @Override - public @NotNull String toString() { - return getClass().getSimpleName(); + public String toString() { + return "ShellExitCommand{" + "usageHelpRequested=" + usageHelpRequested + '}'; } - } diff --git a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java index a931febfa..8faa67ad1 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java @@ -40,4 +40,9 @@ public Integer call() { spec.commandLine().printVersionHelp(System.out); return 0; } + + @Override + public String toString() { + return "VersionCommand{" + "spec=" + spec + '}'; + } } From dd3e3541a29fe54d62bfb86ba8ce6360adfeb735 Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 21 Jul 2022 12:18:15 +0200 Subject: [PATCH 04/64] MQTT Commands > MessagePayloadOptions > Unify toString() method --- .../hivemq/cli/commands/options/MessagePayloadOptions.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java index 03f6a36d9..881e58e36 100644 --- a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java @@ -39,6 +39,11 @@ private void setMessageFromFile(final @NotNull ByteBuffer messageFromFile) { messageBuffer = messageFromFile; } + @Override + public String toString() { + return "MessagePayloadOptions{" + "messageBuffer=" + messageBuffer + '}'; + } + @SuppressWarnings("NotNullFieldNotInitialized") private @NotNull ByteBuffer messageBuffer; From 25e8f56819a6dd0a73689590089a6b65c93f0180 Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 21 Jul 2022 12:50:00 +0200 Subject: [PATCH 05/64] MQTT Commands > Add license headers --- .../cli/commands/options/ConnectOptions.java | 15 +++++++++++++++ .../options/ConnectRestrictionOptions.java | 15 +++++++++++++++ .../hivemq/cli/commands/options/DebugOptions.java | 15 +++++++++++++++ .../cli/commands/options/DisconnectOptions.java | 15 +++++++++++++++ .../cli/commands/options/PublishOptions.java | 15 +++++++++++++++ .../cli/commands/options/SubscribeOptions.java | 15 +++++++++++++++ .../cli/commands/options/UnsubscribeOptions.java | 15 +++++++++++++++ .../hivemq/cli/commands/options/WillOptions.java | 15 +++++++++++++++ src/main/java/com/hivemq/cli/mqtt/ClientKey.java | 15 +++++++++++++++ 9 files changed, 135 insertions(+) diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java index 6d17222aa..e0b3bdded 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import com.google.common.base.Joiner; diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java index 20dbf894e..3de29cc58 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import com.hivemq.client.mqtt.MqttVersion; diff --git a/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java b/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java index f56209b42..918a3a7cf 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import picocli.CommandLine; diff --git a/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java index b3dbda1d3..32de7dc97 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; diff --git a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java index cad10fbdb..ab5bd4596 100644 --- a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import com.hivemq.cli.converters.*; diff --git a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java index c8437c340..1d74e6951 100644 --- a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import com.hivemq.cli.DefaultCLIProperties; diff --git a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java index c66013bc1..1df1cc3e9 100644 --- a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; diff --git a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java index e980f845e..79c816185 100644 --- a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import com.hivemq.cli.converters.*; diff --git a/src/main/java/com/hivemq/cli/mqtt/ClientKey.java b/src/main/java/com/hivemq/cli/mqtt/ClientKey.java index bc7eedef1..8174c4364 100644 --- a/src/main/java/com/hivemq/cli/mqtt/ClientKey.java +++ b/src/main/java/com/hivemq/cli/mqtt/ClientKey.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.mqtt; import com.hivemq.client.mqtt.MqttClient; From 39c6a18a34ddfa5c34a76a55115ec93c8424400d Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 21 Jul 2022 13:54:12 +0200 Subject: [PATCH 06/64] MQTT Commands > Context Subscribe > Fix output --- .../java/com/hivemq/cli/commands/cli/SubscribeCommand.java | 1 + .../hivemq/cli/commands/shell/ContextSubscribeCommand.java | 4 ++-- .../com/hivemq/cli/commands/shell/ShellConnectCommand.java | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java index 366e0e133..b37600195 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java @@ -55,6 +55,7 @@ public class SubscribeCommand implements Callable { description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; + @CommandLine.Mixin private final @NotNull ConnectOptions connectOptions = new ConnectOptions(); diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java index dc154189f..a97995b59 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java @@ -73,8 +73,8 @@ public Integer call() { subscribeOptions.setDefaultOptions(); subscribeOptions.logUnusedOptions(contextClient.getConfig().getMqttVersion()); - if (stay) { - subscribeOptions.setPrintToSTDOUT(true); + if (!stay) { + subscribeOptions.setPrintToSTDOUT(false); } if (subscribeOptions.isOutputFileInvalid(subscribeOptions.getOutputFile())) { diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index 1be45daa5..451a37ada 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -18,7 +18,6 @@ import com.google.common.base.Throwables; import com.hivemq.cli.commands.options.ConnectOptions; -import com.hivemq.cli.commands.options.DebugOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; From b64fbb62e30d46d78b3c991db53c608a0a8a38d0 Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 21 Jul 2022 16:05:39 +0200 Subject: [PATCH 07/64] Logging > Unify logging --- .../cli/commands/cli/PublishCommand.java | 20 ++++++----- .../cli/commands/cli/SubscribeCommand.java | 20 ++++++----- .../commands/shell/ClearScreenCommand.java | 1 - .../shell/ContextDisconnectCommand.java | 3 +- .../commands/shell/ContextPublishCommand.java | 5 +-- .../shell/ContextSubscribeCommand.java | 8 +++-- .../commands/shell/ContextSwitchCommand.java | 3 +- .../shell/ContextUnsubscribeCommand.java | 3 +- .../commands/shell/ShellConnectCommand.java | 9 +++-- .../shell/ShellDisconnectCommand.java | 3 +- .../com/hivemq/cli/utils/LoggerUtils.java | 34 ++++++++++++++++++- 11 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java index a10cede55..ec247adcd 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.cli; -import com.google.common.base.Throwables; import com.hivemq.cli.MqttCLIMain; import com.hivemq.cli.commands.options.ConnectOptions; import com.hivemq.cli.commands.options.DebugOptions; @@ -24,7 +23,6 @@ import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.client.mqtt.MqttClient; -import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import picocli.CommandLine; @@ -82,16 +80,20 @@ public Integer call() { connectOptions.setDefaultOptions(); connectOptions.logUnusedOptions(); publishOptions.logUnusedOptions(connectOptions.getVersion()); + publishOptions.arrangeQosToMatchTopics(); + final MqttClient client; try { - publishOptions.arrangeQosToMatchTopics(); - final MqttClient client = mqttClientExecutor.connect(connectOptions, null); - mqttClientExecutor.publish(client, publishOptions); - } catch (final ConnectionFailedException cex) { - Logger.error(cex, "Unable to connect: {}", cex.getCause().getMessage()); + client = mqttClientExecutor.connect(connectOptions, null); + } catch (final Exception exception) { + LoggerUtils.logCommandError("Unable to connect", exception, debugOptions); return 1; - } catch (final Exception ex) { - Logger.error(ex, "Unable to publish: {}", Throwables.getRootCause(ex).getMessage()); + } + + try { + mqttClientExecutor.publish(client, publishOptions); + } catch (final Exception exception) { + LoggerUtils.logCommandError("Unable to publish", exception, debugOptions); return 1; } diff --git a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java index b37600195..3d019a6c1 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.cli; -import com.google.common.base.Throwables; import com.hivemq.cli.MqttCLIMain; import com.hivemq.cli.commands.options.ConnectOptions; import com.hivemq.cli.commands.options.DebugOptions; @@ -93,27 +92,30 @@ public Integer call() { connectOptions.logUnusedOptions(); subscribeOptions.setDefaultOptions(); subscribeOptions.logUnusedOptions(connectOptions.getVersion()); + subscribeOptions.arrangeQosToMatchTopics(); if (subscribeOptions.isOutputFileInvalid(subscribeOptions.getOutputFile())) { return 1; } try { - subscribeOptions.arrangeQosToMatchTopics(); subscribeClient = mqttClientExecutor.connect(connectOptions, subscribeOptions); - mqttClientExecutor.subscribe(subscribeClient, subscribeOptions); - } catch (final ConnectionFailedException cex) { - Logger.error(cex, "Unable to connect: {}",cex.getCause().getMessage()); + } catch (final Exception exception) { + LoggerUtils.logCommandError("Unable to connect", exception, debugOptions); return 1; - } catch (final Exception ex) { - Logger.error(ex, "Unable to subscribe: {}", Throwables.getRootCause(ex).getMessage()); + } + + try { + mqttClientExecutor.subscribe(subscribeClient, subscribeOptions); + } catch (final ConnectionFailedException exception) { + LoggerUtils.logCommandError("Unable to subscribe", exception, debugOptions); return 1; } try { stay(); - } catch (final InterruptedException ex) { - Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + } catch (final InterruptedException exception) { + LoggerUtils.logCommandError("Unable to stay", exception, debugOptions); return 1; } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java index cf1b52d10..110fd5ed6 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.inject.Inject; diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java index a49f063ac..05f7194cb 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java @@ -20,6 +20,7 @@ import com.hivemq.cli.commands.options.DisconnectOptions; import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; +import com.hivemq.cli.utils.LoggerUtils; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import picocli.CommandLine; @@ -68,7 +69,7 @@ public Integer call() { mqttClientExecutor.disconnect(contextClient, disconnectOptions); } } catch (final Exception ex) { - Logger.error(ex, "Unable to disconnect: {}" ,Throwables.getRootCause(ex).getMessage()); + LoggerUtils.logShellError("Unable to disconnect", ex); return 1; } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java index 01458eb1d..617c97cad 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java @@ -19,6 +19,7 @@ import com.google.common.base.Throwables; import com.hivemq.cli.commands.options.PublishOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; +import com.hivemq.cli.utils.LoggerUtils; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import picocli.CommandLine; @@ -54,12 +55,12 @@ public Integer call() { if (contextClient != null) { publishOptions.logUnusedOptions(contextClient.getConfig().getMqttVersion()); + publishOptions.arrangeQosToMatchTopics(); try { - publishOptions.arrangeQosToMatchTopics(); mqttClientExecutor.publish(contextClient, publishOptions); } catch (final Exception ex) { - Logger.error(ex, "Unable to publish: {}", Throwables.getRootCause(ex).getMessage()); + LoggerUtils.logShellError("Unable to publish", ex); return 1; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java index a97995b59..a6abf998e 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java @@ -20,6 +20,7 @@ import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; +import com.hivemq.cli.utils.LoggerUtils; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import picocli.CommandLine; @@ -72,6 +73,7 @@ public Integer call() { subscribeOptions.setDefaultOptions(); subscribeOptions.logUnusedOptions(contextClient.getConfig().getMqttVersion()); + subscribeOptions.arrangeQosToMatchTopics(); if (!stay) { subscribeOptions.setPrintToSTDOUT(false); @@ -82,17 +84,17 @@ public Integer call() { } try { - subscribeOptions.arrangeQosToMatchTopics(); mqttClientExecutor.subscribe(Objects.requireNonNull(contextClient), subscribeOptions); } catch (final Exception ex) { - Logger.error(ex, "Unable to subscribe: {}", Throwables.getRootCause(ex).getMessage()); + LoggerUtils.logShellError("Unable to subscribe", ex); + return 1; } if (stay) { try { stay(); } catch (final InterruptedException ex) { - Logger.error(ex, "Interrupted while staying after subscribe: {}",Throwables.getRootCause(ex).getMessage()); + LoggerUtils.logShellError("Unable to stay", ex); return 1; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java index 540915f00..30f31a912 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java @@ -19,6 +19,7 @@ import com.google.common.base.Throwables; import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; +import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.client.mqtt.MqttClient; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -72,7 +73,7 @@ public Integer call() { try { extractKeyFromContextName(contextName); } catch (final IllegalArgumentException ex) { - Logger.error(ex, "Unable to switch context: {}", Throwables.getRootCause(ex).getMessage()); + LoggerUtils.logShellError("Unable to switch context", ex); return 1; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java index f1e61fc9b..012750ab1 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java @@ -19,6 +19,7 @@ import com.google.common.base.Throwables; import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; +import com.hivemq.cli.utils.LoggerUtils; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import picocli.CommandLine; @@ -64,7 +65,7 @@ public Integer call() { try { mqttClientExecutor.unsubscribe(contextClient, unsubscribeOptions); } catch (final Exception ex) { - Logger.error(ex, "Unable to unsubscribe: {}", Throwables.getRootCause(ex).getMessage()); + LoggerUtils.logShellError("Unable to unsubscribe", ex); return 1; } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index 451a37ada..0929e975f 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -19,6 +19,7 @@ import com.google.common.base.Throwables; import com.hivemq.cli.commands.options.ConnectOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; +import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.client.mqtt.MqttClient; import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; import org.jetbrains.annotations.NotNull; @@ -58,15 +59,13 @@ public Integer call() { Logger.trace("Command {} ", this); connectOptions.setDefaultOptions(); + connectOptions.logUnusedOptions(); final MqttClient client; try { client = mqttClientExecutor.connect(connectOptions); - } catch (final ConnectionFailedException cex) { - Logger.error(cex, "Unable to connect: {}", cex.getCause().getMessage()); - return 1; - } catch (final Exception ex) { - Logger.error(ex, "Unable to connect: {}", Throwables.getRootCause(ex).getMessage()); + } catch (final Exception exception) { + LoggerUtils.logShellError("Unable to connect", exception); return 1; } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java index 71ab27290..3bac3de3e 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java @@ -21,6 +21,7 @@ import com.hivemq.cli.commands.options.DisconnectOptions; import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; +import com.hivemq.cli.utils.LoggerUtils; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import picocli.CommandLine; @@ -74,7 +75,7 @@ public Integer call() { mqttClientExecutor.disconnect(clientKey, disconnectOptions); } } catch (final Exception ex) { - Logger.error(ex, "Unable to disconnect: {}", Throwables.getRootCause(ex).getMessage()); + LoggerUtils.logShellError("Unable to disconnect", ex); return 1; } diff --git a/src/main/java/com/hivemq/cli/utils/LoggerUtils.java b/src/main/java/com/hivemq/cli/utils/LoggerUtils.java index 98b229933..5e1d14a38 100644 --- a/src/main/java/com/hivemq/cli/utils/LoggerUtils.java +++ b/src/main/java/com/hivemq/cli/utils/LoggerUtils.java @@ -16,13 +16,16 @@ package com.hivemq.cli.utils; +import com.google.common.base.Throwables; import com.hivemq.cli.DefaultCLIProperties; import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.commands.options.DebugOptions; import com.hivemq.client.mqtt.MqttClientConfig; import com.hivemq.client.mqtt.datatypes.MqttClientIdentifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tinylog.Level; +import org.tinylog.Logger; import org.tinylog.configuration.Configuration; import java.io.File; @@ -67,7 +70,11 @@ public static void setupConsoleLogging(final boolean logToLogfile, final @NotNul // TinyLog configuration final Map configurationMap = new HashMap() {{ put("writer1", "console"); - put("writer1.format", "{message-only}"); + if (logLevel.equals("debug") || logLevel.equals("trace")) { + put("writer1.format", "{message}"); + } else { + put("writer1.format", "{message-only}"); + } put("writer1.level", logLevel); }}; @@ -101,4 +108,29 @@ public static String getClientPrefix(final @NotNull MqttClientConfig config) { } return "Client '" + id + "@" + config.getServerHost() + "'"; } + + public static void logCommandError( + final @NotNull String message, + final @NotNull Exception exception, + final @NotNull DebugOptions debugOptions) { + if (debugOptions.isDebug() || debugOptions.isVerbose()) { + Logger.error(exception, message); + } else { + final String exceptionMessage = Throwables.getRootCause(exception).getMessage(); + if (exceptionMessage != null) { + Logger.error("{}. {}", message, exceptionMessage); + } else { + Logger.error("{}. Use '-d' option to get more detailed information.", message); + } + } + } + + public static void logShellError(final @NotNull String message, final @NotNull Exception exception) { + final String exceptionMessage = Throwables.getRootCause(exception).getMessage(); + if (exceptionMessage != null) { + Logger.error(exception, "{}. {}", message, exceptionMessage); + } else { + Logger.error(exception, "{}. Use 'mqtt sh -l' to see more detailed information in the logfile.", message); + } + } } From 3b38928a0de1132a89f7cda7e60b57818aa93580 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 5 Sep 2022 15:32:46 +0200 Subject: [PATCH 08/64] Options > Refactor boolean arguments --- .../java/com/hivemq/cli/commands/options/ConnectOptions.java | 4 ++-- .../java/com/hivemq/cli/commands/options/PublishOptions.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java index e0b3bdded..d6ae51115 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java @@ -65,9 +65,9 @@ public class ConnectOptions { private @Nullable Integer keepAlive; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-c", "--cleanStart"}, negatable = true, + @CommandLine.Option(names = {"-c", "--cleanStart"}, negatable = true, defaultValue = "true", description = "Define a clean start for the connection (default: true)") - private @Nullable Boolean cleanStart; + private boolean cleanStart; @CommandLine.Option(names = {"-se", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, description = "The lifetime of the session of the connected client") diff --git a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java index ab5bd4596..72cc499f8 100644 --- a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java @@ -47,9 +47,9 @@ public class PublishOptions { private @NotNull MessagePayloadOptions message; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--retain"}, negatable = true, + @CommandLine.Option(names = {"-r", "--retain"}, negatable = true, defaultValue = "false", description = "The message will be retained (default: false)") - private @Nullable Boolean retain; + private boolean retain; @SuppressWarnings("unused") @CommandLine.Option(names = {"-e", "--messageExpiryInterval"}, converter = UnsignedIntConverter.class, From d38f54b1a792ca75c387415806a23ab6998f5988 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 5 Sep 2022 16:58:51 +0200 Subject: [PATCH 09/64] System Tests > Fix failing tests --- .../java/com/hivemq/cli/commands/cli/PublishST.java | 7 ++++--- .../com/hivemq/cli/commands/cli/SubscribeST.java | 2 +- .../hivemq/cli/commands/cli/shell/ConnectST.java | 6 ++---- .../java/com/hivemq/cli/utils/CLITestExtension.java | 13 +++++-------- .../java/com/hivemq/cli/utils/CommandConsumer.java | 12 ------------ 5 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index d337cfb6e..8c666411f 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -94,7 +94,7 @@ void test_publish_missing_topic() throws Exception { final Process pub = new ProcessBuilder(publishCommand).start(); - cliTestExtension.waitForErrorWithTimeout(pub, "Missing required option: '--topic '"); + cliTestExtension.waitForErrorWithTimeout(pub, "Missing required option: '--topic='"); assertEquals(pub.waitFor(), 2); } @@ -113,8 +113,9 @@ void test_publish_missing_message() throws Exception { final Process pub = new ProcessBuilder(publishCommand).start(); cliTestExtension.waitForErrorWithTimeout(pub, Set.of( - "Error: Missing required argument (specify one of these): (-m | -m:file )", - "Error: Missing required argument (specify one of these): (-m:file | -m )")); + "Error: Missing required argument (specify one of these): (-m= | -m:file=)", + "Error: Missing required argument (specify one of these): (-m:file= | -m=)")); + assertEquals(pub.waitFor(), 2); } } \ No newline at end of file diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 424c147f4..9738c2ee1 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -92,7 +92,7 @@ void test_subscribe_missing_topic() throws Exception { publishCommand.add(String.valueOf(hivemq.getMqttPort())); final Process sub = new ProcessBuilder(publishCommand).start(); - cliTestExtension.waitForErrorWithTimeout(sub, "Missing required option: '--topic '"); + cliTestExtension.waitForErrorWithTimeout(sub, "Missing required option: '--topic='"); assertEquals(sub.waitFor(), 2); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java index d00736baf..8420c3204 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java @@ -58,9 +58,7 @@ void test_successful_connect() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_unsuccessful_connect() { - cliShellTestExtension.executeCommandWithErrorWithTimeout("con -h localhost -p 22 -i cliTest", Set.of( - "Connection refused: localhost/127.0.0.1:22", - "readAddress(..) failed: Connection reset by peer", - "Connection reset")); + cliShellTestExtension.executeCommandWithErrorWithTimeout("con -h localhost -p 22 -i cliTest", + "Unable to connect. Connection refused"); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java b/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java index e8344d497..fd51e65b0 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java +++ b/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java @@ -30,6 +30,8 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; + public class CLITestExtension { public static final @NotNull List CLI_EXEC = @@ -81,11 +83,6 @@ public void waitForErrorWithTimeout( waitForErrorWithTimeout(process, Set.of(expectedError)); } - public @NotNull CompletableFuture waitForError( - final @NotNull Process process, final @NotNull String expectedError) { - return waitForError(process, Set.of(expectedError)); - } - public void waitForErrorWithTimeout( final @NotNull Process process, final @NotNull Set expectedErrors) { try { @@ -101,14 +98,14 @@ public void waitForErrorWithTimeout( final InputStreamReader errorStreamReader = new InputStreamReader(errorStream, StandardCharsets.UTF_8); final BufferedReader bufferedErrorReader = new BufferedReader(errorStreamReader); - final ArrayList> commandFinished = new ArrayList<>(); + final ArrayList> errorReadFutures = new ArrayList<>(); for (final String expectedError : expectedErrors) { - commandFinished.add(errorConsumer.waitFor(expectedError)); + errorReadFutures.add(errorConsumer.waitFor(expectedError)); } final StringBuilder errorBuilder = new StringBuilder(); return CompletableFuture.runAsync(() -> { - while (commandFinished.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { + while (errorReadFutures.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { try { final int inputChar = bufferedErrorReader.read(); if (inputChar == -1) { diff --git a/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java b/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java index 27c3e2c85..7a85b12e8 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java +++ b/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java @@ -25,16 +25,10 @@ public class CommandConsumer implements Consumer { - private final @NotNull Map> patterns = new ConcurrentHashMap<>(); private final @NotNull Map> contains = new ConcurrentHashMap<>(); @Override public void accept(final @NotNull String commandLine) { - patterns.forEach((pattern, future) -> { - if (commandLine.trim().matches(pattern)) { - future.complete(null); - } - }); contains.forEach((command, future) -> { if (commandLine.contains(command)) { future.complete(null); @@ -42,12 +36,6 @@ public void accept(final @NotNull String commandLine) { }); } - public @NotNull CompletableFuture waitForPattern(final @NotNull String pattern) { - final CompletableFuture future = new CompletableFuture<>(); - patterns.put(pattern, future); - return future; - } - public @NotNull CompletableFuture waitFor(final @NotNull String command) { final CompletableFuture future = new CompletableFuture<>(); contains.put(command, future); From 3f73ccf22acae2bc0c987aa3090242aa1427aba7 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Wed, 7 Sep 2022 14:47:53 +0200 Subject: [PATCH 10/64] Fixed ConnectST. --- .../java/com/hivemq/cli/commands/shell/ShellConnectCommand.java | 2 +- .../java/com/hivemq/cli/commands/cli/shell/ConnectST.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index 0929e975f..a057d155b 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -54,7 +54,7 @@ public ShellConnectCommand(final @NotNull MqttClientExecutor mqttClientExecutor) this.mqttClientExecutor = mqttClientExecutor; } - public Integer call() { + public @NotNull Integer call() { Logger.trace("Command {} ", this); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java index 8420c3204..da47f0427 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java @@ -59,6 +59,6 @@ void test_successful_connect() { @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_unsuccessful_connect() { cliShellTestExtension.executeCommandWithErrorWithTimeout("con -h localhost -p 22 -i cliTest", - "Unable to connect. Connection refused"); + "Unable to connect."); } } From 46e0dcd7d691d59fd6b43ff7d67a72224a4af5c1 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Thu, 8 Sep 2022 17:26:59 +0200 Subject: [PATCH 11/64] Aligned toString. Aligned version and help options. Removed unnecessary inheritance. Fixed minor printout issues. Improved native image code documentation. Code refactoring. --- build.gradle.kts | 4 + .../hivemq/cli/commands/MqttCLICommand.java | 14 ++- .../cli/commands/cli/PublishCommand.java | 22 ++--- .../cli/commands/cli/SubscribeCommand.java | 28 ++---- .../cli/commands/cli/TestBrokerCommand.java | 31 +++---- .../cli/commands/hivemq/HiveMQCLICommand.java | 12 ++- .../hivemq/export/AbstractExportCommand.java | 86 ------------------- .../commands/hivemq/export/ExportCommand.java | 9 ++ .../export/clients/ExportClientsCommand.java | 74 +++++++++++++++- .../options/AuthenticationOptions.java | 10 ++- .../cli/commands/options/ConnectOptions.java | 50 ++++++----- .../options/ConnectRestrictionOptions.java | 3 +- .../cli/commands/options/DebugOptions.java | 4 +- .../cli/commands/options/DefaultOptions.java | 21 +++++ .../commands/options/DisconnectOptions.java | 3 +- .../options/MessagePayloadOptions.java | 10 +-- .../cli/commands/options/PublishOptions.java | 8 +- .../commands/options/SubscribeOptions.java | 16 ++-- .../commands/options/UnsubscribeOptions.java | 30 ++++--- .../cli/commands/options/WillOptions.java | 2 +- .../commands/shell/ClearScreenCommand.java | 13 +-- .../shell/ContextDisconnectCommand.java | 26 ++---- .../commands/shell/ContextExitCommand.java | 18 ++-- .../commands/shell/ContextPublishCommand.java | 22 ++--- .../shell/ContextSubscribeCommand.java | 27 +++--- .../commands/shell/ContextSwitchCommand.java | 26 +++--- .../shell/ContextUnsubscribeCommand.java | 23 ++--- .../commands/shell/ListClientsCommand.java | 20 ++--- .../cli/commands/shell/ShellCommand.java | 29 ++++--- .../commands/shell/ShellConnectCommand.java | 23 ++--- .../commands/shell/ShellContextCommand.java | 11 +-- .../shell/ShellDisconnectCommand.java | 28 +++--- .../cli/commands/shell/ShellExitCommand.java | 12 +-- .../cli/commands/shell/VersionCommand.java | 4 +- .../cli/commands/swarm/SwarmCLICommand.java | 12 ++- .../swarm/commander/SwarmStatusCommand.java | 29 ++++--- .../SwarmOptions.java} | 23 +++-- .../commands/swarm/run/SwarmRunCommand.java | 20 ++++- .../swarm/run/SwarmRunStartCommand.java | 31 ++++--- .../swarm/run/SwarmRunStopCommand.java | 28 +++--- .../cli/utils/CLIShellTestExtension.java | 1 - 41 files changed, 439 insertions(+), 424 deletions(-) delete mode 100644 src/main/java/com/hivemq/cli/commands/hivemq/export/AbstractExportCommand.java create mode 100644 src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java rename src/main/java/com/hivemq/cli/commands/swarm/{AbstractSwarmCommand.java => run/SwarmOptions.java} (72%) diff --git a/build.gradle.kts b/build.gradle.kts index efef82774..d5ae89141 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -463,6 +463,9 @@ cliNative { javaVersion.set(property("java-native.version").toString()) } +//reflection configuration files are currently created manually with the command: ./gradlew -Pagent agentMainRun --stacktrace +//this yields an exception as the Graal plugin is currently quite buggy. The files are created nonetheless. +//build/native/agent-output/agentMainRun/session-*****-*Date*T*Time*Z -> src/main/resources/META-INF/native-image val agentMainRun by tasks.registering(JavaExec::class) { group = "native" @@ -535,6 +538,7 @@ val nativeImageOptions by graalvmNative.binaries.named("main") { graalvmNative { toolchainDetection.set(false) agent { + defaultMode.set("standard") tasksToInstrumentPredicate.set { t -> t == agentMainRun.get() } } binaries { diff --git a/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java b/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java index 40f64744b..3d8476ad8 100644 --- a/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java @@ -17,22 +17,30 @@ package com.hivemq.cli.commands; import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.inject.Inject; @CommandLine.Command(name = "mqtt", description = "MQTT Command Line Interpreter.", - synopsisHeading = "%n@|bold Usage:|@ ", synopsisSubcommandLabel = "{ pub | sub | shell | test | hivemq }", - descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, + synopsisHeading = "%n@|bold Usage:|@ ", + synopsisSubcommandLabel = "{ pub | sub | shell | test | hivemq | swarm }", descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) public class MqttCLICommand { @SuppressWarnings("unused") public static final @NotNull String VERSION_STRING = "1.0"; + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + @Inject MqttCLICommand() {} + @Override + public @NotNull String toString() { + return "MqttCLICommand{" + "defaultOptions=" + defaultOptions + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java index ec247adcd..59bb13c68 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java @@ -19,6 +19,7 @@ import com.hivemq.cli.MqttCLIMain; import com.hivemq.cli.commands.options.ConnectOptions; import com.hivemq.cli.commands.options.DebugOptions; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.PublishOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -34,14 +35,6 @@ description = "Publish a message to a list of topics.") public class PublishCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--version"}, versionHelp = true, description = "display version info") - private boolean versionInfoRequested; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-l"}, defaultValue = "false", description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") @@ -56,6 +49,9 @@ public class PublishCommand implements Callable { @CommandLine.Mixin private final @NotNull DebugOptions debugOptions = new DebugOptions(); + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + private final @NotNull MqttClientExecutor mqttClientExecutor; @Inject @@ -64,7 +60,7 @@ public PublishCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { } @Override - public Integer call() { + public @NotNull Integer call() { String logLevel = "warn"; if (debugOptions.isDebug()) { @@ -101,9 +97,9 @@ public Integer call() { } @Override - public String toString() { - return "PublishCommand{" + "versionInfoRequested=" + versionInfoRequested + ", usageHelpRequested=" + - usageHelpRequested + ", logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + - ", publishOptions=" + publishOptions + ", debugOptions=" + debugOptions + '}'; + public @NotNull String toString() { + return "PublishCommand{" + "logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + + ", publishOptions=" + publishOptions + ", debugOptions=" + debugOptions + ", defaultOptions=" + + defaultOptions + ", mqttClientExecutor=" + mqttClientExecutor + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java index 3d019a6c1..0cdfb1331 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java @@ -19,6 +19,7 @@ import com.hivemq.cli.MqttCLIMain; import com.hivemq.cli.commands.options.ConnectOptions; import com.hivemq.cli.commands.options.DebugOptions; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -41,20 +42,11 @@ public class SubscribeCommand implements Callable { private final @NotNull MqttClientExecutor mqttClientExecutor; private @Nullable MqttClient subscribeClient; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--version"}, versionHelp = true, description = "display version info") - private boolean versionInfoRequested; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-l"}, defaultValue = "false", description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; - @CommandLine.Mixin private final @NotNull ConnectOptions connectOptions = new ConnectOptions(); @@ -64,11 +56,8 @@ public class SubscribeCommand implements Callable { @CommandLine.Mixin private final @NotNull DebugOptions debugOptions = new DebugOptions(); - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public SubscribeCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public SubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @@ -76,7 +65,7 @@ public SubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { } @Override - public Integer call() { + public @NotNull Integer call() { String logLevel = "warn"; if (debugOptions.isDebug()) { logLevel = "debug"; @@ -129,9 +118,10 @@ private void stay() throws InterruptedException { } @Override - public String toString() { - return "SubscribeCommand{" + "versionInfoRequested=" + versionInfoRequested + ", usageHelpRequested=" + - usageHelpRequested + ", logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + - ", subscribeOptions=" + subscribeOptions + ", debugOptions=" + debugOptions + '}'; + public @NotNull String toString() { + return "SubscribeCommand{" + "mqttClientExecutor=" + mqttClientExecutor + ", subscribeClient=" + + subscribeClient + ", logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + + ", subscribeOptions=" + subscribeOptions + ", debugOptions=" + debugOptions + ", defaultOptions=" + + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java b/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java index 0d7ed3bc3..cd8c6e96c 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java @@ -20,6 +20,7 @@ import com.google.common.base.Throwables; import com.hivemq.cli.DefaultCLIProperties; import com.hivemq.cli.commands.options.AuthenticationOptions; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.SslOptions; import com.hivemq.cli.converters.MqttVersionConverter; import com.hivemq.cli.mqtt.test.Mqtt3FeatureTester; @@ -45,20 +46,12 @@ import java.util.concurrent.Callable; @CommandLine.Command(name = "test", - description = "Tests the specified broker on different MQTT feature support and prints the results", + description = "Tests the specified broker on different MQTT feature support and prints the results.", sortOptions = false) public class TestBrokerCommand implements Callable { private static final int MAX_PAYLOAD_TEST_SIZE = 100000; // ~ 1 MB - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--version"}, versionHelp = true, description = "display version info") - private boolean versionInfoRequested; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @CommandLine.Option(names = {"-h", "--host"}, description = "The hostname of the message broker (default 'localhost')", order = 1) private @Nullable String host; @@ -99,23 +92,20 @@ public class TestBrokerCommand implements Callable { @CommandLine.Mixin private final @NotNull SslOptions sslOptions = new SslOptions(); + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + private final @NotNull DefaultCLIProperties defaultCLIProperties; private @Nullable MqttClientSslConfig sslConfig; - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public TestBrokerCommand() { - //noinspection ConstantConditions - this(null); - } - @Inject public TestBrokerCommand(final @NotNull DefaultCLIProperties defaultCLIProperties) { this.defaultCLIProperties = defaultCLIProperties; } @Override - public Integer call() { + public @NotNull Integer call() { LoggerUtils.turnOffConsoleLogging(logToLogfile); Logger.trace("Command {}", this); @@ -411,9 +401,10 @@ public void testMqtt3Features() { @Override public @NotNull String toString() { - return "TestBrokerCommand{" + "MAX_PAYLOAD_TEST_SIZE=" + MAX_PAYLOAD_TEST_SIZE + ", host='" + host + '\'' + - ", port=" + port + ", version=" + version + ", testAll=" + testAll + ", timeOut=" + timeOut + - ", qosTries=" + qosTries + ", logToLogfile=" + logToLogfile + ", authenticationOptions=" + - authenticationOptions + ", sslOptions=" + sslOptions + ", sslConfig=" + sslConfig + '}'; + return "TestBrokerCommand{" + "host='" + host + '\'' + ", port=" + port + ", version=" + version + + ", testAll=" + testAll + ", timeOut=" + timeOut + ", qosTries=" + qosTries + ", logToLogfile=" + + logToLogfile + ", authenticationOptions=" + authenticationOptions + ", sslOptions=" + sslOptions + + ", defaultOptions=" + defaultOptions + ", defaultCLIProperties=" + defaultCLIProperties + + ", sslConfig=" + sslConfig + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java index 9ac3bd3b0..9f5adac80 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java @@ -17,6 +17,7 @@ package com.hivemq.cli.commands.hivemq; import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -25,10 +26,12 @@ @CommandLine.Command(name = "hivemq", description = "HiveMQ Command Line Interpreter.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, - versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) public class HiveMQCLICommand implements Callable { + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -41,4 +44,9 @@ public HiveMQCLICommand() {} System.out.println(spec.commandLine().getUsageMessage(spec.commandLine().getColorScheme())); return 0; } + + @Override + public @NotNull String toString() { + return "HiveMQCLICommand{" + "defaultOptions=" + defaultOptions + ", spec=" + spec + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/AbstractExportCommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/AbstractExportCommand.java deleted file mode 100644 index 6c7f01223..000000000 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/AbstractExportCommand.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.commands.hivemq.export; - -import com.hivemq.cli.utils.LoggerUtils; -import com.opencsv.CSVWriter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import picocli.CommandLine; - -import java.io.File; - -public abstract class AbstractExportCommand { - - public enum OutputFormat { - csv - } - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-url"}, defaultValue = "http://localhost:8888", - description = "The URL of the HiveMQ REST API endpoint (default http://localhost:8888)", order = 1) - protected @NotNull String url; - - @CommandLine.Option(names = {"-f", "--file"}, - description = "The file to write the output to (defaults to a timestamped file in the current working directory)", - order = 2) - protected @Nullable File file; - - @CommandLine.Option(names = {"-r", "--rate"}, defaultValue = "1500", - description = "The rate limit of the rest calls to the HiveMQ API endpoint in requests per second (default 1500 rps)", - order = 3) - protected double rateLimit; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"--format"}, defaultValue = "csv", - description = "The export output format (default csv)", order = 4) - protected @NotNull OutputFormat format; - - @CommandLine.Option(names = {"--csvSeparator"}, defaultValue = "" + CSVWriter.DEFAULT_SEPARATOR, - description = "The separator for CSV export (default " + CSVWriter.DEFAULT_SEPARATOR + ")", order = 5) - public char csvSeparator; - - @CommandLine.Option(names = {"--csvQuoteChar"}, defaultValue = "" + CSVWriter.DEFAULT_QUOTE_CHARACTER, - description = "The quote character for csv export (default " + CSVWriter.DEFAULT_QUOTE_CHARACTER + ")", - order = 6) - protected char csvQuoteCharacter; - - @CommandLine.Option(names = {"--csvEscChar"}, defaultValue = "" + CSVWriter.DEFAULT_ESCAPE_CHARACTER, - description = "The escape character for csv export (default " + CSVWriter.DEFAULT_ESCAPE_CHARACTER + ")", - order = 7) - protected char csvEscapeChar; - - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"--csvLineEndChar"}, defaultValue = CSVWriter.DEFAULT_LINE_END, - description = "The line-end character for csv export (default \\n)", order = 8) - protected @NotNull String csvLineEndCharacter; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", - order = 9) - private void initLogging(final boolean logToLogfile) { - LoggerUtils.turnOffConsoleLogging(logToLogfile); - } - - @Override - public @NotNull String toString() { - return "AbstractExportCommand{" + "url='" + url + '\'' + ", file=" + file + ", rateLimit=" + rateLimit + - ", format=" + format + ", csvSeparator=" + csvSeparator + ", csvQuoteCharacter=" + csvQuoteCharacter + - ", csvEscapeChar=" + csvEscapeChar + ", csvLineEndCharacter='\\n" + '\'' + '}'; - } -} diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java index 2e785e153..5a9d49d11 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java @@ -17,6 +17,7 @@ package com.hivemq.cli.commands.hivemq.export; import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -29,6 +30,9 @@ versionProvider = MqttCLIMain.CLIVersionProvider.class) public class ExportCommand implements Callable { + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -41,4 +45,9 @@ public ExportCommand() {} System.out.println(spec.commandLine().getUsageMessage(spec.commandLine().getColorScheme())); return 0; } + + @Override + public @NotNull String toString() { + return "ExportCommand{" + "defaultOptions=" + defaultOptions + ", spec=" + spec + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java index af428755a..36267b253 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java @@ -19,10 +19,12 @@ import com.google.common.base.Throwables; import com.google.gson.*; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.hivemq.export.AbstractExportCommand; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.openapi.ApiException; import com.hivemq.cli.openapi.hivemq.ClientDetails; import com.hivemq.cli.rest.HiveMQRestService; +import com.hivemq.cli.utils.LoggerUtils; +import com.opencsv.CSVWriter; import okhttp3.HttpUrl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,8 +41,66 @@ import java.util.function.BiFunction; @CommandLine.Command(name = "clients", description = "Export HiveMQ client details", sortOptions = false, - mixinStandardHelpOptions = true, versionProvider = MqttCLIMain.CLIVersionProvider.class) -public class ExportClientsCommand extends AbstractExportCommand implements Callable { + versionProvider = MqttCLIMain.CLIVersionProvider.class) +public class ExportClientsCommand implements Callable { + + public enum OutputFormat { + csv + } + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value + @CommandLine.Option(names = {"-url"}, defaultValue = "http://localhost:8888", + description = "The URL of the HiveMQ REST API endpoint (default http://localhost:8888)", order = 1) + private @NotNull String url; + + @CommandLine.Option(names = {"-f", "--file"}, + description = "The file to write the output to (defaults to a timestamped file in the current working directory)", + order = 2) + private @Nullable File file; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-r", "--rate"}, defaultValue = "1500", + description = "The rate limit of the rest calls to the HiveMQ API endpoint in requests per second (default 1500 rps)", + order = 3) + private double rateLimit; + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value + @CommandLine.Option(names = {"--format"}, defaultValue = "csv", + description = "The export output format (default csv)", order = 4) + private @NotNull OutputFormat format; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"--csvSeparator"}, defaultValue = "" + CSVWriter.DEFAULT_SEPARATOR, + description = "The separator for CSV export (default " + CSVWriter.DEFAULT_SEPARATOR + ")", order = 5) + private char csvSeparator; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"--csvQuoteChar"}, defaultValue = "" + CSVWriter.DEFAULT_QUOTE_CHARACTER, + description = "The quote character for csv export (default " + CSVWriter.DEFAULT_QUOTE_CHARACTER + ")", + order = 6) + private char csvQuoteCharacter; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"--csvEscChar"}, defaultValue = "" + CSVWriter.DEFAULT_ESCAPE_CHARACTER, + description = "The escape character for csv export (default " + CSVWriter.DEFAULT_ESCAPE_CHARACTER + ")", + order = 7) + private char csvEscapeChar; + + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value + @CommandLine.Option(names = {"--csvLineEndChar"}, defaultValue = CSVWriter.DEFAULT_LINE_END, + description = "The line-end character for csv export (default \\n)", order = 8) + private @NotNull String csvLineEndCharacter; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-l"}, defaultValue = "false", + description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", + order = 9) + private void initLogging(final boolean logToLogfile) { + LoggerUtils.turnOffConsoleLogging(logToLogfile); + } + + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); private final static @NotNull String DEFAULT_FILE_NAME = "hivemq_client_details"; private final static int CLIENT_IDS_QUEUE_LIMIT = 100_000; @@ -139,6 +199,14 @@ public ExportClientsCommand() { return exitCode; } + @Override + public @NotNull String toString() { + return "ExportClientsCommand{" + "url='" + url + '\'' + ", file=" + file + ", rateLimit=" + rateLimit + + ", format=" + format + ", csvSeparator=" + csvSeparator + ", csvQuoteCharacter=" + csvQuoteCharacter + + ", csvEscapeChar=" + csvEscapeChar + ", csvLineEndCharacter='" + csvLineEndCharacter + '\'' + + ", defaultOptions=" + defaultOptions + '}'; + } + private static class PrintingTask implements Runnable { private final @NotNull ClientIdsRetrieverTask clientIdsRetrieverTask; diff --git a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java index 91c25dec1..f84023c6e 100644 --- a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java @@ -53,12 +53,16 @@ private void setPasswordFromFile(final @NotNull ByteBuffer passwordFromFile) { password = passwordFromFile; } - public @Nullable String getUser() {return user;} + public @Nullable String getUser() { + return user; + } - public @Nullable ByteBuffer getPassword() {return password;} + public @Nullable ByteBuffer getPassword() { + return password; + } @Override - public String toString() { + public @NotNull String toString() { return "AuthenticationOptions{" + "user='" + user + '\'' + ", password=" + password + '}'; } diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java index d6ae51115..bcbd96ef0 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.options; import com.google.common.base.Joiner; @@ -69,6 +70,7 @@ public class ConnectOptions { description = "Define a clean start for the connection (default: true)") private boolean cleanStart; + @SuppressWarnings("unused") @CommandLine.Option(names = {"-se", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, description = "The lifetime of the session of the connected client") private @Nullable Long sessionExpiryInterval; @@ -78,6 +80,13 @@ public class ConnectOptions { description = "A user property of the connect message'") private @Nullable Mqtt5UserProperty @Nullable [] connectUserProperties; + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-ws"}, description = "Use WebSocket transport protocol (default: false)", order = 2) + private boolean useWebSocket; + + @CommandLine.Option(names = {"-ws:path"}, description = "The path of the WebSocket", order = 2) + private @Nullable String webSocketPath; + @CommandLine.Mixin private final @NotNull WillOptions willOptions = new WillOptions(); @@ -126,28 +135,10 @@ public int getPort() { return connectRestrictionOptions; } - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-ws"}, description = "Use WebSocket transport protocol (default: false)", order = 2) - private boolean useWebSocket; - - @CommandLine.Option(names = {"-ws:path"}, description = "The path of the WebSocket", order = 2) - private @Nullable String webSocketPath; - public @Nullable MqttClientSslConfig buildSslConfig() throws Exception { return sslOptions.buildSslConfig(); } - @Override - public String toString() { - return "ConnectOptions{" + "version=" + version + ", host='" + host + '\'' + ", port=" + port + - ", identifier='" + identifier + '\'' + ", identifierPrefix='" + identifierPrefix + '\'' + - ", keepAlive=" + keepAlive + ", cleanStart=" + cleanStart + ", sessionExpiryInterval=" + - sessionExpiryInterval + ", connectUserProperties=" + Arrays.toString(connectUserProperties) + - ", willOptions=" + willOptions + ", connectRestrictionOptions=" + connectRestrictionOptions + - ", authenticationOptions=" + authenticationOptions + ", sslOptions=" + sslOptions + ", useWebSocket=" + - useWebSocket + ", webSocketPath='" + webSocketPath + '\'' + '}'; - } - public @Nullable Integer getKeepAlive() { return keepAlive; } @@ -165,7 +156,8 @@ public String toString() { } public void setDefaultOptions() { - final DefaultCLIProperties defaultCLIProperties = Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); + final DefaultCLIProperties defaultCLIProperties = + Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); if (version == null) { version = defaultCLIProperties.getMqttVersion(); @@ -201,7 +193,6 @@ public void setDefaultOptions() { } public void logUnusedOptions() { - if (getVersion() == MqttVersion.MQTT_3_1_1) { if (sessionExpiryInterval != null) { Logger.warn("Connect session expiry interval was set but is unused in MQTT Version {}", @@ -224,21 +215,18 @@ public void logUnusedOptions() { for (final MqttUtils.IdentifierWarning warning : warnings) { switch (warning) { case TOO_LONG: - Logger.warn( - "Identifier '{}' may be too long (identifier length '{}' exceeds 23)", + Logger.warn("Identifier '{}' may be too long (identifier length '{}' exceeds 23)", identifier, identifier.length()); break; case TOO_SHORT: - Logger.warn( - "Identifier '{}' may be too short (identifier length '{}' is less than 1)", + Logger.warn("Identifier '{}' may be too short (identifier length '{}' is less than 1)", identifier, identifier.length()); break; case CONTAINS_INVALID_CHAR: final char[] invalidChars = MqttUtils.getInvalidIdChars(identifier); - Logger.warn( - "Identifier '{}' may contain invalid characters ({})", + Logger.warn("Identifier '{}' may contain invalid characters ({})", identifier, "'" + Joiner.on("', '").join(Chars.asList(invalidChars)) + "'"); break; @@ -251,4 +239,14 @@ public void logUnusedOptions() { } } + @Override + public @NotNull String toString() { + return "ConnectOptions{" + "version=" + version + ", host='" + host + '\'' + ", port=" + port + + ", identifier='" + identifier + '\'' + ", identifierPrefix='" + identifierPrefix + '\'' + + ", keepAlive=" + keepAlive + ", cleanStart=" + cleanStart + ", sessionExpiryInterval=" + + sessionExpiryInterval + ", connectUserProperties=" + Arrays.toString(connectUserProperties) + + ", useWebSocket=" + useWebSocket + ", webSocketPath='" + webSocketPath + '\'' + ", willOptions=" + + willOptions + ", connectRestrictionOptions=" + connectRestrictionOptions + ", authenticationOptions=" + + authenticationOptions + ", sslOptions=" + sslOptions + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java index 3de29cc58..d7a226c09 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.options; import com.hivemq.client.mqtt.MqttVersion; @@ -142,7 +143,7 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { } @Override - public String toString() { + public @NotNull String toString() { return "ConnectRestrictionOptions{" + "receiveMaximum=" + receiveMaximum + ", sendMaximum=" + sendMaximum + ", maximumPacketSize=" + maximumPacketSize + ", sendMaximumPacketSize=" + sendMaximumPacketSize + ", topicAliasMaximum=" + topicAliasMaximum + ", sendTopicAliasMaximum=" + sendTopicAliasMaximum + diff --git a/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java b/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java index 918a3a7cf..bab3defe4 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DebugOptions.java @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.options; +import org.jetbrains.annotations.NotNull; import picocli.CommandLine; public class DebugOptions { @@ -50,7 +52,7 @@ public boolean isVerbose() { } @Override - public String toString() { + public @NotNull String toString() { return "DebugOptions{" + "isDebug=" + isDebug + ", isVerbose=" + isVerbose + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java new file mode 100644 index 000000000..82041e4b3 --- /dev/null +++ b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java @@ -0,0 +1,21 @@ +package com.hivemq.cli.commands.options; + +import org.jetbrains.annotations.NotNull; +import picocli.CommandLine; + +public class DefaultOptions { + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"--version"}, versionHelp = true, description = "display version info") + private boolean versionInfoRequested; + + @SuppressWarnings("unused") + @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") + private boolean usageHelpRequested; + + @Override + public @NotNull String toString() { + return "DefaultOptions{" + "versionInfoRequested=" + versionInfoRequested + ", usageHelpRequested=" + + usageHelpRequested + '}'; + } +} diff --git a/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java index 32de7dc97..2ba817b4d 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.options; import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; @@ -103,7 +104,7 @@ public void logUnusedDisconnectOptions(final @NotNull MqttVersion mqttVersion) { } @Override - public String toString() { + public @NotNull String toString() { return "DisconnectOptions{" + "host='" + host + '\'' + ", identifier='" + clientIdentifier + '\'' + ", disconnectAll=" + disconnectAll + ", sessionExpiryInterval=" + sessionExpiryInterval + ", reasonString='" + reasonString + '\'' + ", userProperties=" + Arrays.toString(userProperties) + '}'; diff --git a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java index 881e58e36..402136c2e 100644 --- a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java @@ -39,15 +39,15 @@ private void setMessageFromFile(final @NotNull ByteBuffer messageFromFile) { messageBuffer = messageFromFile; } - @Override - public String toString() { - return "MessagePayloadOptions{" + "messageBuffer=" + messageBuffer + '}'; - } - @SuppressWarnings("NotNullFieldNotInitialized") private @NotNull ByteBuffer messageBuffer; public @NotNull ByteBuffer getMessageBuffer() { return messageBuffer; } + + @Override + public @NotNull String toString() { + return "MessagePayloadOptions{" + "messageBuffer=" + messageBuffer + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java index 72cc499f8..e9051a8f0 100644 --- a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.options; import com.hivemq.cli.converters.*; @@ -28,10 +29,9 @@ import picocli.CommandLine; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; import java.util.Arrays; -public class PublishOptions { +public class PublishOptions { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to publish to") @@ -148,12 +148,12 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { } } - public void arrangeQosToMatchTopics(){ + public void arrangeQosToMatchTopics() { qos = MqttUtils.arrangeQosToMatchTopics(topics, qos); } @Override - public String toString() { + public @NotNull String toString() { return "PublishOptions{" + "topics=" + Arrays.toString(topics) + ", qos=" + Arrays.toString(qos) + ", message=" + message + ", retain=" + retain + ", messageExpiryInterval=" + messageExpiryInterval + ", payloadFormatIndicator=" + payloadFormatIndicator + ", contentType='" + contentType + '\'' + diff --git a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java index 1d74e6951..a88643f37 100644 --- a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.options; import com.hivemq.cli.DefaultCLIProperties; @@ -36,10 +37,6 @@ public class SubscribeOptions { - public SubscribeOptions() { - setDefaultOptions(); - } - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via required @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to subscribe to") private @NotNull String @NotNull [] topics; @@ -79,6 +76,10 @@ public SubscribeOptions() { description = "Prepend the specific topic name to the received publish") private boolean showTopics; + public SubscribeOptions() { + setDefaultOptions(); + } + public @NotNull String @NotNull [] getTopics() { return topics; } @@ -103,7 +104,7 @@ public boolean isPrintToSTDOUT() { return MqttUtils.convertToMqtt5UserProperties(userProperties); } - public @Nullable Mqtt5UserProperty[] getUserPropertiesRaw() { + public @Nullable Mqtt5UserProperty @Nullable [] getUserPropertiesRaw() { return userProperties; } @@ -151,7 +152,8 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { } public void setDefaultOptions() { - final DefaultCLIProperties defaultCLIProperties = Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); + final DefaultCLIProperties defaultCLIProperties = + Objects.requireNonNull(MqttCLIMain.MQTTCLI).defaultCLIProperties(); if (outputFile == null && defaultCLIProperties.getClientSubscribeOutputFile() != null) { Logger.trace("Setting value of 'toFile' to {}", defaultCLIProperties.getClientSubscribeOutputFile()); @@ -160,7 +162,7 @@ public void setDefaultOptions() { } @Override - public String toString() { + public @NotNull String toString() { return "SubscribeOptions{" + "topics=" + Arrays.toString(topics) + ", qos=" + Arrays.toString(qos) + ", userProperties=" + Arrays.toString(userProperties) + ", outputFile=" + outputFile + ", printToSTDOUT=" + printToSTDOUT + ", base64=" + base64 + ", jsonOutput=" + jsonOutput + diff --git a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java index 1df1cc3e9..51b2b9021 100644 --- a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.options; import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; @@ -24,17 +25,13 @@ import org.tinylog.Logger; import picocli.CommandLine; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; public class UnsubscribeOptions { - public UnsubscribeOptions() { - } - - public UnsubscribeOptions(final @NotNull String @NotNull [] topics, final @Nullable Mqtt5UserProperty @Nullable [] userProperties) { - this.topics = topics; - this.userProperties = userProperties; - } - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Option(names = {"-t", "--topic"}, required = true, description = "The topics to publish to") private @NotNull String @NotNull [] topics; @@ -42,15 +39,26 @@ public UnsubscribeOptions(final @NotNull String @NotNull [] topics, final @Nulla @SuppressWarnings("unused") @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, description = "A user property for the unsubscribe message") - private @NotNull Mqtt5UserProperty @Nullable [] userProperties; + private @Nullable Mqtt5UserProperty @Nullable [] userProperties; + + public UnsubscribeOptions() { + } + + public UnsubscribeOptions( + final @NotNull String @NotNull [] topics, final @Nullable Mqtt5UserProperty @Nullable [] userProperties) { + this.topics = topics; + this.userProperties = userProperties; + } - public @NotNull String[] getTopics() { + public @NotNull String @NotNull [] getTopics() { return topics; } public @NotNull Mqtt5UserProperties getUserProperties() { if (userProperties != null && userProperties.length > 0) { - return Mqtt5UserProperties.of(userProperties); + final List nonNullProperties = + Arrays.stream(userProperties).filter(Objects::nonNull).collect(Collectors.toList()); + return Mqtt5UserProperties.of(nonNullProperties); } else { return Mqtt5UserProperties.of(); } diff --git a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java index 79c816185..fb8e2d257 100644 --- a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java @@ -161,7 +161,7 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { } @Override - public String toString() { + public @NotNull String toString() { return "WillOptions{" + "willTopic='" + willTopic + '\'' + ", willMessage=" + willMessage + ", willQos=" + willQos + ", willRetain=" + willRetain + ", willMessageExpiryInterval=" + willMessageExpiryInterval + ", willDelayInterval=" + willDelayInterval + ", willPayloadFormatIndicator=" + diff --git a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java index 110fd5ed6..9f468ea5e 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java @@ -16,6 +16,8 @@ package com.hivemq.cli.commands.shell; +import com.hivemq.cli.commands.options.DefaultOptions; +import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.inject.Inject; @@ -27,21 +29,20 @@ @CommandLine.Command(name = "cls", aliases = "clear", description = "Clear the screen") public class ClearScreenCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject ClearScreenCommand() {} @Override - public Integer call() { + public @NotNull Integer call() { ShellCommand.clearScreen(); return 0; } @Override - public String toString() { - return "ClearScreenCommand{" + "usageHelpRequested=" + usageHelpRequested + '}'; + public @NotNull String toString() { + return "ClearScreenCommand{" + "defaultOptions=" + defaultOptions + '}'; } } \ No newline at end of file diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java index 05f7194cb..dbf948c0c 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.shell; -import com.google.common.base.Throwables; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.DisconnectOptions; import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; @@ -31,19 +31,11 @@ @CommandLine.Command(name = "dis", aliases = "disconnect", description = "Disconnects this MQTT client") public class ContextDisconnectCommand extends ShellContextCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @CommandLine.Mixin private final @NotNull DisconnectOptions disconnectOptions = new DisconnectOptions(); - - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ContextDisconnectCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public ContextDisconnectCommand(final @NotNull MqttClientExecutor executor) { @@ -51,8 +43,7 @@ public ContextDisconnectCommand(final @NotNull MqttClientExecutor executor) { } @Override - public Integer call() { - + public @NotNull Integer call() { Logger.trace("Command {} ", this); if (contextClient != null) { @@ -63,7 +54,8 @@ public Integer call() { if (disconnectOptions.isDisconnectAll()) { mqttClientExecutor.disconnectAllClients(disconnectOptions); } else if (disconnectOptions.getClientIdentifier() != null && disconnectOptions.getHost() != null) { - final ClientKey clientKey = ClientKey.of(disconnectOptions.getClientIdentifier(), disconnectOptions.getHost()); + final ClientKey clientKey = + ClientKey.of(disconnectOptions.getClientIdentifier(), disconnectOptions.getHost()); mqttClientExecutor.disconnect(clientKey, disconnectOptions); } else if (contextClient != null) { mqttClientExecutor.disconnect(contextClient, disconnectOptions); @@ -77,8 +69,8 @@ public Integer call() { } @Override - public String toString() { - return "ContextDisconnectCommand{" + "usageHelpRequested=" + usageHelpRequested + ", disconnectOptions=" + - disconnectOptions + '}'; + public @NotNull String toString() { + return "ContextDisconnectCommand{" + "disconnectOptions=" + disconnectOptions + ", defaultOptions=" + + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java index 6440dd2da..bc9486397 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java @@ -16,6 +16,7 @@ package com.hivemq.cli.commands.shell; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -26,15 +27,8 @@ @CommandLine.Command(name = "exit", description = "Exit the current context") public class ContextExitCommand extends ShellContextCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ContextExitCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public ContextExitCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @@ -42,13 +36,13 @@ public ContextExitCommand(final @NotNull MqttClientExecutor mqttClientExecutor) } @Override - public Integer call() { + public @NotNull Integer call() { removeContext(); return 0; } @Override - public String toString() { - return "ContextExitCommand{" + "usageHelpRequested=" + usageHelpRequested + '}'; + public @NotNull String toString() { + return "ContextExitCommand{" + "defaultOptions=" + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java index 617c97cad..18f0dddc7 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.shell; -import com.google.common.base.Throwables; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.PublishOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -30,18 +30,11 @@ @CommandLine.Command(name = "pub", aliases = "publish", description = "Publish a message to a list of topics") public class ContextPublishCommand extends ShellContextCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - boolean usageHelpRequested; - @CommandLine.Mixin private final @NotNull PublishOptions publishOptions = new PublishOptions(); - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ContextPublishCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public ContextPublishCommand(final @NotNull MqttClientExecutor executor) { @@ -49,8 +42,7 @@ public ContextPublishCommand(final @NotNull MqttClientExecutor executor) { } @Override - public Integer call() { - + public @NotNull Integer call() { Logger.trace("Command {} ", this); if (contextClient != null) { @@ -69,8 +61,8 @@ public Integer call() { } @Override - public String toString() { - return "ContextPublishCommand{" + "usageHelpRequested=" + usageHelpRequested + ", publishOptions=" + - publishOptions + '}'; + public @NotNull String toString() { + return "ContextPublishCommand{" + "publishOptions=" + publishOptions + ", defaultOptions=" + defaultOptions + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java index a6abf998e..00fe9fad0 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.shell; -import com.google.common.base.Throwables; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; @@ -39,22 +39,16 @@ public class ContextSubscribeCommand extends ShellContextCommand implements Call private static final int IDLE_TIME = 1000; - @CommandLine.Mixin - private final @NotNull SubscribeOptions subscribeOptions = new SubscribeOptions(); - - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - boolean usageHelpRequested; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-s", "--stay"}, defaultValue = "false", description = "The subscribe will block the console and wait for publish messages to print (default: false)") private boolean stay; - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ContextSubscribeCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull SubscribeOptions subscribeOptions = new SubscribeOptions(); + + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public ContextSubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @@ -62,8 +56,7 @@ public ContextSubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecu } @Override - public Integer call() { - + public @NotNull Integer call() { Logger.trace("Command {} ", this); if (contextClient == null) { @@ -141,8 +134,8 @@ private void stay() throws InterruptedException { } @Override - public String toString() { - return "ContextSubscribeCommand{" + "subscribeOptions=" + subscribeOptions + ", usageHelpRequested=" + - usageHelpRequested + ", stay=" + stay + '}'; + public @NotNull String toString() { + return "ContextSubscribeCommand{" + "stay=" + stay + ", subscribeOptions=" + subscribeOptions + + ", defaultOptions=" + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java index 30f31a912..bf3495540 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.shell; -import com.google.common.base.Throwables; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -27,15 +27,12 @@ import picocli.CommandLine; import javax.inject.Inject; +import java.util.Objects; import java.util.concurrent.Callable; @CommandLine.Command(name = "switch", description = "Switch the current context") public class ContextSwitchCommand extends ShellContextCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @SuppressWarnings("unused") @CommandLine.Parameters(index = "0", arity = "0..1", description = "The name of the context, e.g. client@localhost") private @Nullable String contextName; @@ -48,11 +45,8 @@ public class ContextSwitchCommand extends ShellContextCommand implements Callabl description = "The hostname of the message broker (default 'localhost')") private @Nullable String host; - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ContextSwitchCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public ContextSwitchCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @@ -60,8 +54,7 @@ public ContextSwitchCommand(final @NotNull MqttClientExecutor mqttClientExecutor } @Override - public Integer call() { - + public @NotNull Integer call() { Logger.trace("Command {} ", this); if (contextName == null && identifier == null) { @@ -78,7 +71,8 @@ public Integer call() { } } - final MqttClient client = mqttClientExecutor.getMqttClient(ClientKey.of(identifier, host)); + final MqttClient client = + mqttClientExecutor.getMqttClient(ClientKey.of(identifier, Objects.requireNonNull(host))); if (client != null) { updateContext(client); @@ -104,8 +98,8 @@ private void extractKeyFromContextName(final String contextName) { } @Override - public String toString() { - return "ContextSwitchCommand{" + "usageHelpRequested=" + usageHelpRequested + ", contextName='" + contextName + - '\'' + ", identifier='" + identifier + '\'' + ", host='" + host + '\'' + '}'; + public @NotNull String toString() { + return "ContextSwitchCommand{" + "contextName='" + contextName + '\'' + ", identifier='" + identifier + '\'' + + ", host='" + host + '\'' + ", defaultOptions=" + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java index 012750ab1..aa8c48f8c 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.shell; -import com.google.common.base.Throwables; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -31,19 +31,11 @@ description = "Unsubscribe this MQTT client from a list of topics") public class ContextUnsubscribeCommand extends ShellContextCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @CommandLine.Mixin private final @NotNull UnsubscribeOptions unsubscribeOptions = new UnsubscribeOptions(); - - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ContextUnsubscribeCommand() { - //noinspection ConstantConditions - this(null); - } + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public ContextUnsubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @@ -51,8 +43,7 @@ public ContextUnsubscribeCommand(final @NotNull MqttClientExecutor mqttClientExe } @Override - public Integer call() { - + public @NotNull Integer call() { Logger.trace("Command {} ", this); if (contextClient == null) { @@ -73,8 +64,8 @@ public Integer call() { } @Override - public String toString() { - return "ContextUnsubscribeCommand{" + "usageHelpRequested=" + usageHelpRequested + ", unsubscribeOptions=" + - unsubscribeOptions + '}'; + public @NotNull String toString() { + return "ContextUnsubscribeCommand{" + "unsubscribeOptions=" + unsubscribeOptions + ", defaultOptions=" + + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java index c3bf3e411..b71acae23 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java @@ -16,6 +16,7 @@ package com.hivemq.cli.commands.shell; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.ClientData; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; @@ -34,10 +35,6 @@ description = "List all connected clients with their respective identifiers") public class ListClientsCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-t"}, defaultValue = "false", description = "sort by creation time, newest first") private boolean sortByTime; @@ -60,14 +57,15 @@ public class ListClientsCommand implements Callable { description = "list subscribed topics of clients") private boolean listSubscriptions; + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + @Inject - //needed for pico cli - reflection code generation public ListClientsCommand() { } @Override - public Integer call() { - + public @NotNull Integer call() { Logger.trace("Command {}", this); final List sortedClientData = getSortedClientData(); @@ -179,9 +177,9 @@ public Integer call() { } @Override - public String toString() { - return "ListClientsCommand{" + "usageHelpRequested=" + usageHelpRequested + ", sortByTime=" + sortByTime + - ", doNotSort=" + doNotSort + ", reverse=" + reverse + ", longOutput=" + longOutput + - ", listSubscriptions=" + listSubscriptions + '}'; + public @NotNull String toString() { + return "ListClientsCommand{" + "sortByTime=" + sortByTime + ", doNotSort=" + doNotSort + ", reverse=" + + reverse + ", longOutput=" + longOutput + ", listSubscriptions=" + listSubscriptions + + ", defaultOptions=" + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java index 6fca7c1f5..60f7a0327 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java @@ -19,6 +19,7 @@ import com.google.common.base.Throwables; import com.hivemq.cli.DefaultCLIProperties; import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.client.mqtt.datatypes.MqttClientIdentifier; import org.jetbrains.annotations.NotNull; @@ -41,13 +42,14 @@ import javax.inject.Inject; import java.io.PrintWriter; import java.util.Objects; +import java.util.concurrent.Callable; @CommandLine.Command(name = "shell", aliases = "sh", versionProvider = MqttCLIMain.CLIVersionProvider.class, description = "Starts MqttCLI in shell mode, to enable interactive mode with further sub commands.", footer = {"", "@|bold Press Ctl-C to exit.|@"}, synopsisHeading = "%n@|bold Usage|@: ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options|@:%n", commandListHeading = "%n@|bold Commands|@:%n", separator = " ") -public class ShellCommand implements Runnable { +public class ShellCommand implements Callable { private static final @NotNull String DEFAULT_PROMPT = "mqtt> "; private static @NotNull String prompt = DEFAULT_PROMPT; @@ -62,20 +64,15 @@ public class ShellCommand implements Runnable { private static @Nullable CommandLine contextCommandLine; private static boolean exitShell = false; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--version", "-V"}, versionHelp = true, description = "display version info") - private boolean versionInfoRequested; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help", "-h"}, usageHelp = true, description = "display this help message") - private boolean usageHelpRequested; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-l"}, defaultValue = "false", description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", order = 1) private boolean logToLogfile; + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -90,14 +87,14 @@ public class ShellCommand implements Runnable { } @Override - public void run() { + public @NotNull Integer call() { LoggerUtils.setupConsoleLogging(logToLogfile, "warn"); logfilePath = Configuration.get("writer.file"); - interact(); + return interact(); } - private void interact() { + private @NotNull Integer interact() { shellCommandLine = Objects.requireNonNull(MqttCLIMain.MQTTCLI).shell(); contextCommandLine = MqttCLIMain.MQTTCLI.shellContext(); @@ -149,15 +146,18 @@ private void interact() { } } catch (final UserInterruptException e) { Logger.trace("--- User interrupted shell ---"); - return; + return 0; } catch (final Exception ex) { Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + return 1; } } Logger.info("--- Shell-Mode exited ---"); } catch (final Exception ex) { Logger.error(ex, Throwables.getRootCause(ex).getMessage()); + return 1; } + return 0; } static void exitShell() { @@ -202,6 +202,7 @@ static void clearScreen() { @Override public @NotNull String toString() { - return getClass().getSimpleName() + "{" + "logfilePath=" + logfilePath + "}"; + return "ShellCommand{" + "logToLogfile=" + logToLogfile + ", defaultOptions=" + defaultOptions + ", spec=" + + spec + ", defaultCLIProperties=" + defaultCLIProperties + ", logfilePath='" + logfilePath + '\'' + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index a057d155b..a059cba55 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -16,12 +16,11 @@ package com.hivemq.cli.commands.shell; -import com.google.common.base.Throwables; import com.hivemq.cli.commands.options.ConnectOptions; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.client.mqtt.MqttClient; -import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; import picocli.CommandLine; @@ -33,21 +32,13 @@ abbreviateSynopsis = true) public class ShellConnectCommand implements Callable { - private final @NotNull MqttClientExecutor mqttClientExecutor; - - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") - boolean usageHelpRequested; - @CommandLine.Mixin private final @NotNull ConnectOptions connectOptions = new ConnectOptions(); + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ShellConnectCommand() { - //noinspection ConstantConditions - this(null); - } + private final @NotNull MqttClientExecutor mqttClientExecutor; @Inject public ShellConnectCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @@ -75,8 +66,8 @@ public ShellConnectCommand(final @NotNull MqttClientExecutor mqttClientExecutor) } @Override - public String toString() { - return "ShellConnectCommand{" + "usageHelpRequested=" + usageHelpRequested + ", connectOptions=" + - connectOptions + '}'; + public @NotNull String toString() { + return "ShellConnectCommand{" + "connectOptions=" + connectOptions + ", defaultOptions=" + defaultOptions + + ", mqttClientExecutor=" + mqttClientExecutor + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java index 128806231..4bc0487b9 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java @@ -16,6 +16,7 @@ package com.hivemq.cli.commands.shell; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; import org.jetbrains.annotations.NotNull; @@ -37,12 +38,6 @@ public class ShellContextCommand implements Callable { @NotNull MqttClientExecutor mqttClientExecutor; - //needed for pico cli - reflection code generation - public ShellContextCommand() { - //noinspection ConstantConditions - this(null); - } - @Inject public ShellContextCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { this.mqttClientExecutor = mqttClientExecutor; @@ -61,13 +56,13 @@ public static void removeContext() { } @Override - public Integer call() { + public @NotNull Integer call() { Objects.requireNonNull(ShellCommand.TERMINAL_WRITER).println(ShellCommand.getUsageMessage()); return 0; } @Override - public String toString() { + public @NotNull String toString() { return "ShellContextCommand{}"; } } \ No newline at end of file diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java index 3bac3de3e..16fdf23eb 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java @@ -16,8 +16,8 @@ package com.hivemq.cli.commands.shell; -import com.google.common.base.Throwables; import com.hivemq.cli.DefaultCLIProperties; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.DisconnectOptions; import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; @@ -32,22 +32,15 @@ @CommandLine.Command(name = "dis", aliases = "disconnect", description = "Disconnect an MQTT client") public class ShellDisconnectCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"--help"}, usageHelp = true, description = "display this help message") - boolean usageHelpRequested; - @CommandLine.Mixin private final @NotNull DisconnectOptions disconnectOptions = new DisconnectOptions(); + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + private final @NotNull MqttClientExecutor mqttClientExecutor; private final @NotNull DefaultCLIProperties defaultCLIProperties; - @SuppressWarnings("unused") //needed for pico cli - reflection code generation - public ShellDisconnectCommand() { - //noinspection ConstantConditions - this(null, null); - } - @Inject ShellDisconnectCommand( final @NotNull MqttClientExecutor mqttClientExecutor, @@ -57,7 +50,7 @@ public ShellDisconnectCommand() { } @Override - public Integer call() { + public @NotNull Integer call() { if (disconnectOptions.getHost() == null) { disconnectOptions.setHost(defaultCLIProperties.getHost()); } @@ -71,20 +64,21 @@ public Integer call() { Logger.error("Missing required option '--identifier='"); return 1; } - final ClientKey clientKey = ClientKey.of(disconnectOptions.getClientIdentifier(), disconnectOptions.getHost()); + final ClientKey clientKey = + ClientKey.of(disconnectOptions.getClientIdentifier(), disconnectOptions.getHost()); mqttClientExecutor.disconnect(clientKey, disconnectOptions); } } catch (final Exception ex) { LoggerUtils.logShellError("Unable to disconnect", ex); return 1; } - return 0; } @Override - public String toString() { - return "ShellDisconnectCommand{" + "usageHelpRequested=" + usageHelpRequested + ", disconnectOptions=" + - disconnectOptions + '}'; + public @NotNull String toString() { + return "ShellDisconnectCommand{" + "disconnectOptions=" + disconnectOptions + ", defaultOptions=" + + defaultOptions + ", mqttClientExecutor=" + mqttClientExecutor + ", defaultCLIProperties=" + + defaultCLIProperties + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java index 001c2f2cb..f37801973 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java @@ -16,6 +16,7 @@ package com.hivemq.cli.commands.shell; +import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -25,23 +26,22 @@ @CommandLine.Command(name = "exit", description = "Exit the shell") public class ShellExitCommand implements Callable { - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - boolean usageHelpRequested; + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); @Inject public ShellExitCommand() { } @Override - public Integer call() { + public @NotNull Integer call() { ShellCommand.exitShell(); return 0; } @Override - public String toString() { - return "ShellExitCommand{" + "usageHelpRequested=" + usageHelpRequested + '}'; + public @NotNull String toString() { + return "ShellExitCommand{" + "defaultOptions=" + defaultOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java index 8faa67ad1..ec207c36d 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java @@ -36,13 +36,13 @@ public class VersionCommand implements Callable { } @Override - public Integer call() { + public @NotNull Integer call() { spec.commandLine().printVersionHelp(System.out); return 0; } @Override - public String toString() { + public @NotNull String toString() { return "VersionCommand{" + "spec=" + spec + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java b/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java index 1be079d5b..3fefd040d 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java @@ -17,6 +17,7 @@ package com.hivemq.cli.commands.swarm; import com.hivemq.cli.MqttCLIMain; +import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -25,10 +26,12 @@ @CommandLine.Command(name = "swarm", description = "HiveMQ Swarm Command Line Interpreter.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, - versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) public class SwarmCLICommand implements Callable { + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -41,4 +44,9 @@ public SwarmCLICommand() {} System.out.println(spec.commandLine().getUsageMessage(spec.commandLine().getColorScheme())); return 0; } + + @Override + public @NotNull String toString() { + return "SwarmCLICommand{" + "defaultOptions=" + defaultOptions + ", spec=" + spec + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java index fefd01d02..8ed124751 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java @@ -18,9 +18,10 @@ import com.google.gson.Gson; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.swarm.AbstractSwarmCommand; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.swarm.error.Error; import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; +import com.hivemq.cli.commands.swarm.run.SwarmOptions; import com.hivemq.cli.openapi.ApiException; import com.hivemq.cli.openapi.swarm.CommanderApi; import com.hivemq.cli.openapi.swarm.CommanderStateResponse; @@ -34,13 +35,13 @@ import javax.inject.Inject; import javax.inject.Provider; import java.io.PrintStream; +import java.util.concurrent.Callable; @CommandLine.Command(name = "status", description = "Check the status of HiveMQ Swarm. (READY, STARTING, RUNNING, STOPPING).", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, - versionProvider = MqttCLIMain.CLIVersionProvider.class) -public class SwarmStatusCommand extends AbstractSwarmCommand { + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) +public class SwarmStatusCommand implements Callable { public enum OutputFormat { JSON, @@ -52,6 +53,12 @@ public enum OutputFormat { description = "The export output format (JSON, PRETTY). Default=PRETTY.", order = 2) private @NotNull OutputFormat format = OutputFormat.PRETTY; + @CommandLine.Mixin + private final @NotNull SwarmOptions swarmOptions = new SwarmOptions(); + + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + private final @NotNull Gson gson; private final @NotNull RunsApi runsApi; private final @NotNull CommanderApi commanderApi; @@ -78,15 +85,15 @@ public SwarmStatusCommand( Logger.trace("Command {}", this); // Check if given URL is valid - final HttpUrl httpUrl = HttpUrl.parse(commanderUrl); + final HttpUrl httpUrl = HttpUrl.parse(swarmOptions.getCommanderUrl()); if (httpUrl == null) { - Logger.error("URL is not in a valid format: {}", commanderUrl); - System.err.println("URL is not in a valid format: " + commanderUrl); + Logger.error("URL is not in a valid format: {}", swarmOptions.getCommanderUrl()); + System.err.println("URL is not in a valid format: " + swarmOptions.getCommanderUrl()); return -1; } - runsApi.getApiClient().setBasePath(commanderUrl); - commanderApi.getApiClient().setBasePath(commanderUrl); + runsApi.getApiClient().setBasePath(swarmOptions.getCommanderUrl()); + commanderApi.getApiClient().setBasePath(swarmOptions.getCommanderUrl()); final CommanderStateResponse commanderStatus; try { @@ -152,6 +159,8 @@ public SwarmStatusCommand( @Override public @NotNull String toString() { - return "SwarmStatusCommand{" + "format=" + format + '}'; + return "SwarmStatusCommand{" + "format=" + format + ", swarmOptions=" + swarmOptions + ", defaultOptions=" + + defaultOptions + ", gson=" + gson + ", runsApi=" + runsApi + ", commanderApi=" + commanderApi + + ", errorTransformer=" + errorTransformer + ", out=" + out + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/AbstractSwarmCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmOptions.java similarity index 72% rename from src/main/java/com/hivemq/cli/commands/swarm/AbstractSwarmCommand.java rename to src/main/java/com/hivemq/cli/commands/swarm/run/SwarmOptions.java index 7965ec3f1..dc92a4611 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/AbstractSwarmCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmOptions.java @@ -14,19 +14,18 @@ * limitations under the License. */ -package com.hivemq.cli.commands.swarm; +package com.hivemq.cli.commands.swarm.run; import com.hivemq.cli.utils.LoggerUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.VisibleForTesting; import picocli.CommandLine; -import java.util.concurrent.Callable; - -public abstract class AbstractSwarmCommand implements Callable { +public class SwarmOptions { @CommandLine.Option(names = {"-url"}, defaultValue = "http://localhost:8080", description = "The URL of the HiveMQ Swarm REST API endpoint (default: http://localhost:8080)", order = 1) - protected @NotNull String commanderUrl = "http://localhost:8080"; + private @NotNull String commanderUrl = "http://localhost:8080"; @SuppressWarnings("unused") @CommandLine.Option(names = {"-l"}, defaultValue = "false", @@ -35,8 +34,20 @@ private void initLogging(final boolean logToLogfile) { LoggerUtils.turnOffConsoleLogging(logToLogfile); } + public SwarmOptions() { + } + + @VisibleForTesting + public SwarmOptions(final @NotNull String commanderUrl) { + this.commanderUrl = commanderUrl; + } + + public @NotNull String getCommanderUrl() { + return commanderUrl; + } + @Override public @NotNull String toString() { - return "AbstractSwarmCommand{" + "url='" + commanderUrl + '\'' + '}'; + return "SwarmOptions{" + "url='" + commanderUrl + '\'' + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java index 75793c6e9..d4a2ca3ee 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java @@ -17,22 +17,28 @@ package com.hivemq.cli.commands.swarm.run; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.swarm.AbstractSwarmCommand; +import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.inject.Inject; +import java.util.concurrent.Callable; @CommandLine.Command(name = "run", description = "HiveMQ Swarm Run Command Line Interpreter.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, - versionProvider = MqttCLIMain.CLIVersionProvider.class) -public class SwarmRunCommand extends AbstractSwarmCommand { + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) +public class SwarmRunCommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; + @CommandLine.Mixin + private final @NotNull SwarmOptions swarmOptions = new SwarmOptions(); + + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + @Inject public SwarmRunCommand() {} @@ -41,4 +47,10 @@ public SwarmRunCommand() {} System.out.println(spec.commandLine().getUsageMessage(spec.commandLine().getColorScheme())); return 0; } + + @Override + public @NotNull String toString() { + return "SwarmRunCommand{" + "spec=" + spec + ", swarmOptions=" + swarmOptions + ", defaultOptions=" + + defaultOptions + '}'; + } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java index 88e7217ac..fe68a36d2 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java @@ -17,7 +17,7 @@ package com.hivemq.cli.commands.swarm.run; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.swarm.AbstractSwarmCommand; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.swarm.error.Error; import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; import com.hivemq.cli.openapi.ApiException; @@ -37,13 +37,13 @@ import java.nio.file.Files; import java.util.Base64; import java.util.UUID; +import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @CommandLine.Command(name = "start", description = "Start HiveMQ Swarm runs.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, - versionProvider = MqttCLIMain.CLIVersionProvider.class) -public class SwarmRunStartCommand extends AbstractSwarmCommand { + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) +public class SwarmRunStartCommand implements Callable { @CommandLine.Option(names = {"-f", "--file"}, description = "The scenario file. " + "If a scenario file is given this command uploads, executes and deletes the scenario afterwards.", @@ -56,6 +56,12 @@ public class SwarmRunStartCommand extends AbstractSwarmCommand { "The scenario is not deleted afterwards.", order = 4) private @NotNull Boolean detached = false; + @CommandLine.Mixin + private @NotNull SwarmOptions swarmOptions = new SwarmOptions(); + + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + private final @NotNull RunsApi runsApi; private final @NotNull ScenariosApi scenariosApi; private final @NotNull SwarmApiErrorTransformer errorTransformer; @@ -83,7 +89,7 @@ public SwarmRunStartCommand( final @NotNull SwarmApiErrorTransformer errorTransformer, final @NotNull PrintStream out) { this(() -> runsApi, () -> scenariosApi, errorTransformer, out); - this.commanderUrl = commanderUrl; + this.swarmOptions = new SwarmOptions(commanderUrl); this.scenario = scenario; this.detached = detached; } @@ -93,15 +99,15 @@ public SwarmRunStartCommand( Logger.trace("Command {}", this); // Check if given URL is valid - final HttpUrl httpUrl = HttpUrl.parse(commanderUrl); + final HttpUrl httpUrl = HttpUrl.parse(swarmOptions.getCommanderUrl()); if (httpUrl == null) { - Logger.error("URL is not in a valid format: {}", commanderUrl); - System.err.println("URL is not in a valid format: " + commanderUrl); + Logger.error("URL is not in a valid format: {}", swarmOptions.getCommanderUrl()); + System.err.println("URL is not in a valid format: " + swarmOptions.getCommanderUrl()); return -1; } - runsApi.getApiClient().setBasePath(commanderUrl); - scenariosApi.getApiClient().setBasePath(commanderUrl); + runsApi.getApiClient().setBasePath(swarmOptions.getCommanderUrl()); + scenariosApi.getApiClient().setBasePath(swarmOptions.getCommanderUrl()); if (scenario == null) { Logger.error("Scenario file is missing. Option '-f' is not set"); @@ -248,7 +254,8 @@ private boolean isTerminated(final @NotNull RunResponse run) { @Override public @NotNull String toString() { - return "SwarmRunStartCommand{" + "commanderUrl='" + commanderUrl + '\'' + ", scenario=" + - (scenario != null ? scenario.getAbsolutePath() : null) + ", detached=" + detached + '}'; + return "SwarmRunStartCommand{" + "scenario=" + scenario + ", detached=" + detached + ", swarmOptions=" + + swarmOptions + ", defaultOptions=" + defaultOptions + ", runsApi=" + runsApi + ", scenariosApi=" + + scenariosApi + ", errorTransformer=" + errorTransformer + ", out=" + out + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java index 5306fcb18..d73e5937e 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java @@ -17,7 +17,7 @@ package com.hivemq.cli.commands.swarm.run; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.swarm.AbstractSwarmCommand; +import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.swarm.error.Error; import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; import com.hivemq.cli.openapi.ApiException; @@ -35,17 +35,23 @@ import javax.inject.Inject; import javax.inject.Provider; import java.io.PrintStream; +import java.util.concurrent.Callable; @CommandLine.Command(name = "stop", description = "Stop HiveMQ Swarm runs.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, - versionProvider = MqttCLIMain.CLIVersionProvider.class) -public class SwarmRunStopCommand extends AbstractSwarmCommand { + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) +public class SwarmRunStopCommand implements Callable { @CommandLine.Option(names = {"-r", "--run-id"}, description = "The id of the run to stop. If none is given the current run is stopped.", order = 3) private @Nullable Integer runId; + @CommandLine.Mixin + private @NotNull SwarmOptions swarmOptions = new SwarmOptions(); + + @CommandLine.Mixin + private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); + private final @NotNull RunsApi runsApi; private final @NotNull CommanderApi commanderApi; private final @NotNull SwarmApiErrorTransformer errorTransformer; @@ -72,7 +78,7 @@ public SwarmRunStopCommand( final @NotNull SwarmApiErrorTransformer errorTransformer, final @NotNull PrintStream out) { this(() -> runsApi, () -> commanderApi, errorTransformer, out); - this.commanderUrl = commanderUrl; + this.swarmOptions = new SwarmOptions(commanderUrl); this.runId = runId; } @@ -81,14 +87,14 @@ public SwarmRunStopCommand( Logger.trace("Command {}", this); // Check if given URL is valid - final HttpUrl httpUrl = HttpUrl.parse(commanderUrl); + final HttpUrl httpUrl = HttpUrl.parse(swarmOptions.getCommanderUrl()); if (httpUrl == null) { - Logger.error("URL is not in a valid format: {}", commanderUrl); - System.err.println("URL is not in a valid format: " + commanderUrl); + Logger.error("URL is not in a valid format: {}", swarmOptions.getCommanderUrl()); + System.err.println("URL is not in a valid format: " + swarmOptions.getCommanderUrl()); return -1; } - runsApi.getApiClient().setBasePath(commanderUrl); + runsApi.getApiClient().setBasePath(swarmOptions.getCommanderUrl()); final int usedRunID; if (runId == null) { @@ -126,6 +132,8 @@ public SwarmRunStopCommand( @Override public @NotNull String toString() { - return "SwarmRunStopCommand{" + "commanderUrl='" + commanderUrl + '\'' + ", runId=" + runId + '}'; + return "SwarmRunStopCommand{" + "runId=" + runId + ", swarmOptions=" + swarmOptions + ", defaultOptions=" + + defaultOptions + ", runsApi=" + runsApi + ", commanderApi=" + commanderApi + ", errorTransformer=" + + errorTransformer + ", out=" + out + '}'; } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java b/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java index bf855013d..a7a878d52 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java +++ b/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java @@ -202,7 +202,6 @@ public void executeCommandWithErrorWithTimeout( } errorBuilder.append((char) inputChar); errorConsumer.accept(errorBuilder.toString()); - System.out.println(errorBuilder); } catch (final IOException e) { throw new RuntimeException(e); } From 44b872caf836c4e33159756dbe5de485051152c3 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Wed, 14 Sep 2022 15:46:16 +0200 Subject: [PATCH 12/64] Aligned version and help options. --- .../com/hivemq/cli/commands/MqttCLICommand.java | 8 ++------ .../cli/commands/hivemq/HiveMQCLICommand.java | 9 +++------ .../cli/commands/hivemq/export/ExportCommand.java | 10 +++------- .../hivemq/export/clients/ExportClientsCommand.java | 9 ++------- .../hivemq/cli/commands/options/DefaultOptions.java | 4 ++++ .../cli/commands/shell/ClearScreenCommand.java | 8 ++------ .../cli/commands/shell/ContextExitCommand.java | 8 ++------ .../cli/commands/shell/ContextPublishCommand.java | 10 +++------- .../cli/commands/shell/ContextSubscribeCommand.java | 9 ++------- .../cli/commands/shell/ContextSwitchCommand.java | 8 ++------ .../commands/shell/ContextUnsubscribeCommand.java | 9 ++------- .../cli/commands/shell/ListClientsCommand.java | 9 ++------- .../com/hivemq/cli/commands/shell/ShellCommand.java | 10 +++------- .../cli/commands/shell/ShellContextCommand.java | 1 - .../hivemq/cli/commands/shell/ShellExitCommand.java | 8 ++------ .../hivemq/cli/commands/swarm/SwarmCLICommand.java | 9 +++------ .../swarm/commander/SwarmStatusCommand.java | 13 +++++-------- .../cli/commands/swarm/run/SwarmRunCommand.java | 10 +++------- .../commands/swarm/run/SwarmRunStartCommand.java | 11 ++++------- .../cli/commands/swarm/run/SwarmRunStopCommand.java | 12 ++++-------- .../java/com/hivemq/cli/utils/CLITestExtension.java | 2 -- 21 files changed, 53 insertions(+), 124 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java b/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java index 3d8476ad8..f6521326c 100644 --- a/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java @@ -17,7 +17,6 @@ package com.hivemq.cli.commands; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -27,20 +26,17 @@ synopsisHeading = "%n@|bold Usage:|@ ", synopsisSubcommandLabel = "{ pub | sub | shell | test | hivemq | swarm }", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", commandListHeading = "%n@|bold Commands:|@%n", - versionProvider = MqttCLIMain.CLIVersionProvider.class) + versionProvider = MqttCLIMain.CLIVersionProvider.class, mixinStandardHelpOptions = true) public class MqttCLICommand { @SuppressWarnings("unused") public static final @NotNull String VERSION_STRING = "1.0"; - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject MqttCLICommand() {} @Override public @NotNull String toString() { - return "MqttCLICommand{" + "defaultOptions=" + defaultOptions + '}'; + return "MqttCLICommand{}"; } } diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java index 9f5adac80..05ef46412 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java @@ -17,7 +17,6 @@ package com.hivemq.cli.commands.hivemq; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -26,12 +25,10 @@ @CommandLine.Command(name = "hivemq", description = "HiveMQ Command Line Interpreter.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class HiveMQCLICommand implements Callable { - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -47,6 +44,6 @@ public HiveMQCLICommand() {} @Override public @NotNull String toString() { - return "HiveMQCLICommand{" + "defaultOptions=" + defaultOptions + ", spec=" + spec + '}'; + return "HiveMQCLICommand{" + ", spec=" + spec + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java index 5a9d49d11..6d087cbb8 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java @@ -17,7 +17,6 @@ package com.hivemq.cli.commands.hivemq.export; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -26,13 +25,10 @@ @CommandLine.Command(name = "export", description = "Exports the specified details from HiveMQ", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", mixinStandardHelpOptions = true, - versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class ExportCommand implements Callable { - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -48,6 +44,6 @@ public ExportCommand() {} @Override public @NotNull String toString() { - return "ExportCommand{" + "defaultOptions=" + defaultOptions + ", spec=" + spec + '}'; + return "ExportCommand{" + ", spec=" + spec + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java index 36267b253..e14840c46 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java @@ -19,7 +19,6 @@ import com.google.common.base.Throwables; import com.google.gson.*; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.openapi.ApiException; import com.hivemq.cli.openapi.hivemq.ClientDetails; import com.hivemq.cli.rest.HiveMQRestService; @@ -41,7 +40,7 @@ import java.util.function.BiFunction; @CommandLine.Command(name = "clients", description = "Export HiveMQ client details", sortOptions = false, - versionProvider = MqttCLIMain.CLIVersionProvider.class) + versionProvider = MqttCLIMain.CLIVersionProvider.class, mixinStandardHelpOptions = true) public class ExportClientsCommand implements Callable { public enum OutputFormat { @@ -99,9 +98,6 @@ private void initLogging(final boolean logToLogfile) { LoggerUtils.turnOffConsoleLogging(logToLogfile); } - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - private final static @NotNull String DEFAULT_FILE_NAME = "hivemq_client_details"; private final static int CLIENT_IDS_QUEUE_LIMIT = 100_000; private final static int CLIENT_DETAILS_QUEUE_LIMIT = 10_000; @@ -203,8 +199,7 @@ public ExportClientsCommand() { public @NotNull String toString() { return "ExportClientsCommand{" + "url='" + url + '\'' + ", file=" + file + ", rateLimit=" + rateLimit + ", format=" + format + ", csvSeparator=" + csvSeparator + ", csvQuoteCharacter=" + csvQuoteCharacter + - ", csvEscapeChar=" + csvEscapeChar + ", csvLineEndCharacter='" + csvLineEndCharacter + '\'' + - ", defaultOptions=" + defaultOptions + '}'; + ", csvEscapeChar=" + csvEscapeChar + ", csvLineEndCharacter='" + csvLineEndCharacter + '\'' + '}'; } private static class PrintingTask implements Runnable { diff --git a/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java index 82041e4b3..aa41fef7d 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java @@ -3,6 +3,10 @@ import org.jetbrains.annotations.NotNull; import picocli.CommandLine; +/** + * Helper Option in order to allow the --version and --help option at commands which already define -h and -V + * themselves. + */ public class DefaultOptions { @SuppressWarnings("unused") diff --git a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java index 9f468ea5e..6a33e813d 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -26,12 +25,9 @@ /** * Command that clears the screen. */ -@CommandLine.Command(name = "cls", aliases = "clear", description = "Clear the screen") +@CommandLine.Command(name = "cls", aliases = "clear", description = "Clear the screen", mixinStandardHelpOptions = true) public class ClearScreenCommand implements Callable { - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject ClearScreenCommand() {} @@ -43,6 +39,6 @@ public class ClearScreenCommand implements Callable { @Override public @NotNull String toString() { - return "ClearScreenCommand{" + "defaultOptions=" + defaultOptions + '}'; + return "ClearScreenCommand{}"; } } \ No newline at end of file diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java index bc9486397..1681d0aa3 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextExitCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -24,12 +23,9 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "exit", description = "Exit the current context") +@CommandLine.Command(name = "exit", description = "Exit the current context", mixinStandardHelpOptions = true) public class ContextExitCommand extends ShellContextCommand implements Callable { - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public ContextExitCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { super(mqttClientExecutor); @@ -43,6 +39,6 @@ public ContextExitCommand(final @NotNull MqttClientExecutor mqttClientExecutor) @Override public @NotNull String toString() { - return "ContextExitCommand{" + "defaultOptions=" + defaultOptions + '}'; + return "ContextExitCommand{}"; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java index 18f0dddc7..1b6aca399 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.PublishOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -27,15 +26,13 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "pub", aliases = "publish", description = "Publish a message to a list of topics") +@CommandLine.Command(name = "pub", aliases = "publish", description = "Publish a message to a list of topics", + mixinStandardHelpOptions = true) public class ContextPublishCommand extends ShellContextCommand implements Callable { @CommandLine.Mixin private final @NotNull PublishOptions publishOptions = new PublishOptions(); - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public ContextPublishCommand(final @NotNull MqttClientExecutor executor) { super(executor); @@ -62,7 +59,6 @@ public ContextPublishCommand(final @NotNull MqttClientExecutor executor) { @Override public @NotNull String toString() { - return "ContextPublishCommand{" + "publishOptions=" + publishOptions + ", defaultOptions=" + defaultOptions + - '}'; + return "ContextPublishCommand{" + "publishOptions=" + publishOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java index 00fe9fad0..763f1ae8d 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; @@ -34,7 +33,7 @@ import java.util.concurrent.Executors; @CommandLine.Command(name = "sub", aliases = "subscribe", - description = "Subscribe this MQTT client to a list of topics") + description = "Subscribe this MQTT client to a list of topics", mixinStandardHelpOptions = true) public class ContextSubscribeCommand extends ShellContextCommand implements Callable { private static final int IDLE_TIME = 1000; @@ -47,9 +46,6 @@ public class ContextSubscribeCommand extends ShellContextCommand implements Call @CommandLine.Mixin private final @NotNull SubscribeOptions subscribeOptions = new SubscribeOptions(); - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public ContextSubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { super(mqttClientExecutor); @@ -135,7 +131,6 @@ private void stay() throws InterruptedException { @Override public @NotNull String toString() { - return "ContextSubscribeCommand{" + "stay=" + stay + ", subscribeOptions=" + subscribeOptions + - ", defaultOptions=" + defaultOptions + '}'; + return "ContextSubscribeCommand{" + "stay=" + stay + ", subscribeOptions=" + subscribeOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java index bf3495540..c647d83c2 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -30,7 +29,7 @@ import java.util.Objects; import java.util.concurrent.Callable; -@CommandLine.Command(name = "switch", description = "Switch the current context") +@CommandLine.Command(name = "switch", description = "Switch the current context", mixinStandardHelpOptions = true) public class ContextSwitchCommand extends ShellContextCommand implements Callable { @SuppressWarnings("unused") @@ -45,9 +44,6 @@ public class ContextSwitchCommand extends ShellContextCommand implements Callabl description = "The hostname of the message broker (default 'localhost')") private @Nullable String host; - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public ContextSwitchCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { super(mqttClientExecutor); @@ -100,6 +96,6 @@ private void extractKeyFromContextName(final String contextName) { @Override public @NotNull String toString() { return "ContextSwitchCommand{" + "contextName='" + contextName + '\'' + ", identifier='" + identifier + '\'' + - ", host='" + host + '\'' + ", defaultOptions=" + defaultOptions + '}'; + ", host='" + host + '\'' + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java index aa8c48f8c..35eb81815 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.cli.utils.LoggerUtils; @@ -28,15 +27,12 @@ import java.util.concurrent.Callable; @CommandLine.Command(name = "unsub", aliases = "unsubscribe", - description = "Unsubscribe this MQTT client from a list of topics") + description = "Unsubscribe this MQTT client from a list of topics", mixinStandardHelpOptions = true) public class ContextUnsubscribeCommand extends ShellContextCommand implements Callable { @CommandLine.Mixin private final @NotNull UnsubscribeOptions unsubscribeOptions = new UnsubscribeOptions(); - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public ContextUnsubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { super(mqttClientExecutor); @@ -65,7 +61,6 @@ public ContextUnsubscribeCommand(final @NotNull MqttClientExecutor mqttClientExe @Override public @NotNull String toString() { - return "ContextUnsubscribeCommand{" + "unsubscribeOptions=" + unsubscribeOptions + ", defaultOptions=" + - defaultOptions + '}'; + return "ContextUnsubscribeCommand{" + "unsubscribeOptions=" + unsubscribeOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java index b71acae23..9f753351d 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.ClientData; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; @@ -32,7 +31,7 @@ import java.util.stream.Collectors; @CommandLine.Command(name = "ls", aliases = "list", - description = "List all connected clients with their respective identifiers") + description = "List all connected clients with their respective identifiers", mixinStandardHelpOptions = true) public class ListClientsCommand implements Callable { @SuppressWarnings("unused") @@ -57,9 +56,6 @@ public class ListClientsCommand implements Callable { description = "list subscribed topics of clients") private boolean listSubscriptions; - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public ListClientsCommand() { } @@ -179,7 +175,6 @@ public ListClientsCommand() { @Override public @NotNull String toString() { return "ListClientsCommand{" + "sortByTime=" + sortByTime + ", doNotSort=" + doNotSort + ", reverse=" + - reverse + ", longOutput=" + longOutput + ", listSubscriptions=" + listSubscriptions + - ", defaultOptions=" + defaultOptions + '}'; + reverse + ", longOutput=" + longOutput + ", listSubscriptions=" + listSubscriptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java index 60f7a0327..4b348f383 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java @@ -19,7 +19,6 @@ import com.google.common.base.Throwables; import com.hivemq.cli.DefaultCLIProperties; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.client.mqtt.datatypes.MqttClientIdentifier; import org.jetbrains.annotations.NotNull; @@ -48,7 +47,7 @@ description = "Starts MqttCLI in shell mode, to enable interactive mode with further sub commands.", footer = {"", "@|bold Press Ctl-C to exit.|@"}, synopsisHeading = "%n@|bold Usage|@: ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options|@:%n", - commandListHeading = "%n@|bold Commands|@:%n", separator = " ") + commandListHeading = "%n@|bold Commands|@:%n", separator = " ", mixinStandardHelpOptions = true) public class ShellCommand implements Callable { private static final @NotNull String DEFAULT_PROMPT = "mqtt> "; @@ -70,9 +69,6 @@ public class ShellCommand implements Callable { order = 1) private boolean logToLogfile; - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -202,7 +198,7 @@ static void clearScreen() { @Override public @NotNull String toString() { - return "ShellCommand{" + "logToLogfile=" + logToLogfile + ", defaultOptions=" + defaultOptions + ", spec=" + - spec + ", defaultCLIProperties=" + defaultCLIProperties + ", logfilePath='" + logfilePath + '\'' + '}'; + return "ShellCommand{" + "logToLogfile=" + logToLogfile + ", spec=" + spec + ", defaultCLIProperties=" + + defaultCLIProperties + ", logfilePath='" + logfilePath + '\'' + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java index 4bc0487b9..5877eaa71 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java @@ -16,7 +16,6 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java index f37801973..5fa1763c4 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellExitCommand.java @@ -16,19 +16,15 @@ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "exit", description = "Exit the shell") +@CommandLine.Command(name = "exit", description = "Exit the shell", mixinStandardHelpOptions = true) public class ShellExitCommand implements Callable { - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public ShellExitCommand() { } @@ -41,7 +37,7 @@ public ShellExitCommand() { @Override public @NotNull String toString() { - return "ShellExitCommand{" + "defaultOptions=" + defaultOptions + '}'; + return "ShellExitCommand{}"; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java b/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java index 3fefd040d..01c4608de 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java @@ -17,7 +17,6 @@ package com.hivemq.cli.commands.swarm; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -26,12 +25,10 @@ @CommandLine.Command(name = "swarm", description = "HiveMQ Swarm Command Line Interpreter.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmCLICommand implements Callable { - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @CommandLine.Spec private @NotNull CommandLine.Model.CommandSpec spec; @@ -47,6 +44,6 @@ public SwarmCLICommand() {} @Override public @NotNull String toString() { - return "SwarmCLICommand{" + "defaultOptions=" + defaultOptions + ", spec=" + spec + '}'; + return "SwarmCLICommand{" + ", spec=" + spec + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java index 8ed124751..48cebbad0 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java @@ -18,7 +18,6 @@ import com.google.gson.Gson; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.swarm.error.Error; import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; import com.hivemq.cli.commands.swarm.run.SwarmOptions; @@ -40,7 +39,8 @@ @CommandLine.Command(name = "status", description = "Check the status of HiveMQ Swarm. (READY, STARTING, RUNNING, STOPPING).", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmStatusCommand implements Callable { public enum OutputFormat { @@ -56,9 +56,6 @@ public enum OutputFormat { @CommandLine.Mixin private final @NotNull SwarmOptions swarmOptions = new SwarmOptions(); - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - private final @NotNull Gson gson; private final @NotNull RunsApi runsApi; private final @NotNull CommanderApi commanderApi; @@ -159,8 +156,8 @@ public SwarmStatusCommand( @Override public @NotNull String toString() { - return "SwarmStatusCommand{" + "format=" + format + ", swarmOptions=" + swarmOptions + ", defaultOptions=" + - defaultOptions + ", gson=" + gson + ", runsApi=" + runsApi + ", commanderApi=" + commanderApi + - ", errorTransformer=" + errorTransformer + ", out=" + out + '}'; + return "SwarmStatusCommand{" + "format=" + format + ", swarmOptions=" + swarmOptions + ", gson=" + gson + + ", runsApi=" + runsApi + ", commanderApi=" + commanderApi + ", errorTransformer=" + errorTransformer + + ", out=" + out + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java index d4a2ca3ee..984344905 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java @@ -17,7 +17,6 @@ package com.hivemq.cli.commands.swarm.run; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import org.jetbrains.annotations.NotNull; import picocli.CommandLine; @@ -26,7 +25,8 @@ @CommandLine.Command(name = "run", description = "HiveMQ Swarm Run Command Line Interpreter.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmRunCommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @@ -36,9 +36,6 @@ public class SwarmRunCommand implements Callable { @CommandLine.Mixin private final @NotNull SwarmOptions swarmOptions = new SwarmOptions(); - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - @Inject public SwarmRunCommand() {} @@ -50,7 +47,6 @@ public SwarmRunCommand() {} @Override public @NotNull String toString() { - return "SwarmRunCommand{" + "spec=" + spec + ", swarmOptions=" + swarmOptions + ", defaultOptions=" + - defaultOptions + '}'; + return "SwarmRunCommand{" + "spec=" + spec + ", swarmOptions=" + swarmOptions + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java index fe68a36d2..84794d868 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java @@ -17,7 +17,6 @@ package com.hivemq.cli.commands.swarm.run; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.swarm.error.Error; import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; import com.hivemq.cli.openapi.ApiException; @@ -42,7 +41,8 @@ @CommandLine.Command(name = "start", description = "Start HiveMQ Swarm runs.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmRunStartCommand implements Callable { @CommandLine.Option(names = {"-f", "--file"}, description = "The scenario file. " + @@ -59,9 +59,6 @@ public class SwarmRunStartCommand implements Callable { @CommandLine.Mixin private @NotNull SwarmOptions swarmOptions = new SwarmOptions(); - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - private final @NotNull RunsApi runsApi; private final @NotNull ScenariosApi scenariosApi; private final @NotNull SwarmApiErrorTransformer errorTransformer; @@ -255,7 +252,7 @@ private boolean isTerminated(final @NotNull RunResponse run) { @Override public @NotNull String toString() { return "SwarmRunStartCommand{" + "scenario=" + scenario + ", detached=" + detached + ", swarmOptions=" + - swarmOptions + ", defaultOptions=" + defaultOptions + ", runsApi=" + runsApi + ", scenariosApi=" + - scenariosApi + ", errorTransformer=" + errorTransformer + ", out=" + out + '}'; + swarmOptions + ", runsApi=" + runsApi + ", scenariosApi=" + scenariosApi + ", errorTransformer=" + + errorTransformer + ", out=" + out + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java index d73e5937e..3e3c3b628 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java @@ -17,7 +17,6 @@ package com.hivemq.cli.commands.swarm.run; import com.hivemq.cli.MqttCLIMain; -import com.hivemq.cli.commands.options.DefaultOptions; import com.hivemq.cli.commands.swarm.error.Error; import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; import com.hivemq.cli.openapi.ApiException; @@ -39,7 +38,8 @@ @CommandLine.Command(name = "stop", description = "Stop HiveMQ Swarm runs.", synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class) + commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmRunStopCommand implements Callable { @CommandLine.Option(names = {"-r", "--run-id"}, @@ -49,9 +49,6 @@ public class SwarmRunStopCommand implements Callable { @CommandLine.Mixin private @NotNull SwarmOptions swarmOptions = new SwarmOptions(); - @CommandLine.Mixin - private final @NotNull DefaultOptions defaultOptions = new DefaultOptions(); - private final @NotNull RunsApi runsApi; private final @NotNull CommanderApi commanderApi; private final @NotNull SwarmApiErrorTransformer errorTransformer; @@ -132,8 +129,7 @@ public SwarmRunStopCommand( @Override public @NotNull String toString() { - return "SwarmRunStopCommand{" + "runId=" + runId + ", swarmOptions=" + swarmOptions + ", defaultOptions=" + - defaultOptions + ", runsApi=" + runsApi + ", commanderApi=" + commanderApi + ", errorTransformer=" + - errorTransformer + ", out=" + out + '}'; + return "SwarmRunStopCommand{" + "runId=" + runId + ", swarmOptions=" + swarmOptions + ", runsApi=" + runsApi + + ", commanderApi=" + commanderApi + ", errorTransformer=" + errorTransformer + ", out=" + out + '}'; } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java b/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java index fd51e65b0..e467fafd2 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java +++ b/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java @@ -30,8 +30,6 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -import static org.testcontainers.shaded.org.awaitility.Awaitility.await; - public class CLITestExtension { public static final @NotNull List CLI_EXEC = From edb67272bc846bde6a14f7098b53a8ee11953f87 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Wed, 14 Sep 2022 15:55:43 +0200 Subject: [PATCH 13/64] Added license header to DefaultOptions. --- .../cli/commands/options/DefaultOptions.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java index aa41fef7d..98e051d9e 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java @@ -1,3 +1,19 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.options; import org.jetbrains.annotations.NotNull; From 53be9b4dd012fe7a90e7ac441e6b632de7ab1930 Mon Sep 17 00:00:00 2001 From: gitseti Date: Fri, 30 Sep 2022 17:46:30 +0200 Subject: [PATCH 14/64] WIP: System Tests > Refactor tests --- .../commands/shell/ShellConnectCommand.java | 1 - .../hivemq/cli/commands/cli/PublishST.java | 90 ++++---- .../hivemq/cli/commands/cli/SubscribeST.java | 57 +++-- .../cli/commands/cli/shell/ConnectST.java | 35 ++- .../cli/commands/cli/shell/DisconnectST.java | 24 +- .../cli/commands/cli/shell/PublishST.java | 49 ++-- .../cli/commands/cli/shell/SubscribeST.java | 36 +-- .../com/hivemq/cli/utils/AwaitOutput.java | 71 ++++++ .../cli/utils/CLIShellTestExtension.java | 211 ------------------ .../hivemq/cli/utils/CLITestExtension.java | 120 ---------- .../com/hivemq/cli/utils/CommandConsumer.java | 44 ---- .../com/hivemq/cli/utils/ExecutionResult.java | 42 ++++ .../java/com/hivemq/cli/utils/MqttCli.java | 63 ++++++ .../com/hivemq/cli/utils/MqttCliShell.java | 73 ++++++ .../java/com/hivemq/cli/utils/ProcessIO.java | 156 +++++++++++++ 15 files changed, 555 insertions(+), 517 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java delete mode 100644 src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java delete mode 100644 src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java delete mode 100644 src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/MqttCli.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index a059cba55..5f8a19fa0 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -46,7 +46,6 @@ public ShellConnectCommand(final @NotNull MqttClientExecutor mqttClientExecutor) } public @NotNull Integer call() { - Logger.trace("Command {} ", this); connectOptions.setDefaultOptions(); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index 8c666411f..3b374d7c9 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -16,7 +16,8 @@ package com.hivemq.cli.commands.cli; -import com.hivemq.cli.utils.CLITestExtension; +import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; @@ -27,21 +28,19 @@ import org.junit.jupiter.api.Timeout; import org.testcontainers.utility.DockerImageName; -import java.io.IOException; -import java.util.ArrayList; import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class PublishST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); - private final @NotNull CLITestExtension cliTestExtension = new CLITestExtension(); + private final @NotNull MqttCli mqttCli = new MqttCli(); @BeforeAll static void beforeAll() { @@ -56,17 +55,14 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_publish() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("pub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - publishCommand.add("-t"); - publishCommand.add("test"); - publishCommand.add("-m"); - publishCommand.add("test"); - publishCommand.add("-d"); + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-t", "test", + "-m", "test", + "-d" + ); final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() .identifier("subscriber") @@ -74,48 +70,48 @@ void test_successful_publish() throws Exception { .serverPort(hivemq.getMqttPort()) .buildBlocking(); subscriber.connect(); - final CompletableFuture testReturn = new CompletableFuture<>(); - subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> testReturn.complete(null)).send(); - final Process pub = new ProcessBuilder(publishCommand).start(); + final CountDownLatch receivedPublish = new CountDownLatch(1); + subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> receivedPublish.countDown()).send(); - cliTestExtension.waitForOutputWithTimeout(pub, "received PUBLISH acknowledgement"); - testReturn.get(10, TimeUnit.SECONDS); + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertTrue(receivedPublish.await(10, TimeUnit.SECONDS)); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); + assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); + assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_topic() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("pub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()) + ); - final Process pub = new ProcessBuilder(publishCommand).start(); + final ExecutionResult executionResult = mqttCli.execute(publishCommand); - cliTestExtension.waitForErrorWithTimeout(pub, "Missing required option: '--topic='"); - assertEquals(pub.waitFor(), 2); + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic='")); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_message() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("pub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - publishCommand.add("-t"); - publishCommand.add("test"); - - final Process pub = new ProcessBuilder(publishCommand).start(); - - cliTestExtension.waitForErrorWithTimeout(pub, Set.of( - "Error: Missing required argument (specify one of these): (-m= | -m:file=)", - "Error: Missing required argument (specify one of these): (-m:file= | -m=)")); - - assertEquals(pub.waitFor(), 2); + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-t", "test" + ); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m= | -m:file=)") + || executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m:file= | -m=)")); } } \ No newline at end of file diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 9738c2ee1..808b9b48b 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -16,7 +16,9 @@ package com.hivemq.cli.commands.cli; -import com.hivemq.cli.utils.CLITestExtension; +import com.hivemq.cli.utils.AwaitOutput; +import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; @@ -27,21 +29,19 @@ import org.junit.jupiter.api.Timeout; import org.testcontainers.utility.DockerImageName; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SubscribeST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq-ce")); - private final @NotNull CLITestExtension cliTestExtension = new CLITestExtension(); + private final @NotNull MqttCli mqttCli = new MqttCli(); @BeforeAll static void beforeAll() { @@ -56,17 +56,13 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_subscribe() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("sub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - publishCommand.add("-t"); - publishCommand.add("test"); - publishCommand.add("-d"); - - final Process sub = new ProcessBuilder(publishCommand).start(); + final List subscribeCommand = List.of( + "sub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-t", "test", + "-d" + ); final Mqtt5BlockingClient publisher = Mqtt5Client.builder() .identifier("publisher") @@ -75,24 +71,27 @@ void test_successful_subscribe() throws Exception { .buildBlocking(); publisher.connect(); - cliTestExtension.waitForOutputWithTimeout(sub, "received SUBACK"); - final CompletableFuture testReturn = cliTestExtension.waitForOutput(sub, "testReturn"); + final AwaitOutput awaitOutput = mqttCli.executeAsync(subscribeCommand); + + awaitOutput.awaitStdout("received SUBACK"); + publisher.publishWith().topic("test").payload("testReturn".getBytes(StandardCharsets.UTF_8)).send(); - testReturn.get(10, TimeUnit.SECONDS); + + awaitOutput.awaitStdout("testReturn"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_subscribe_missing_topic() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("sub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - final Process sub = new ProcessBuilder(publishCommand).start(); - - cliTestExtension.waitForErrorWithTimeout(sub, "Missing required option: '--topic='"); - assertEquals(sub.waitFor(), 2); + final List subscribeCommand = List.of( + "sub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()) + ); + + final ExecutionResult executionResult = mqttCli.execute(subscribeCommand); + + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic='")); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java index da47f0427..31f8e84d2 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,7 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; -import java.util.Set; +import java.util.List; import java.util.concurrent.TimeUnit; public class ConnectST { @@ -35,7 +35,7 @@ public class ConnectST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + final MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -49,16 +49,31 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_connect() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); + void test_successful_connect() throws Exception { + final List connectCommand = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-i", "cliTest" + ); + + mqttCliShell.executeCommand(connectCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); } + @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_unsuccessful_connect() { - cliShellTestExtension.executeCommandWithErrorWithTimeout("con -h localhost -p 22 -i cliTest", - "Unable to connect."); + void test_unsuccessful_connect() throws Exception { + final List connectCommand = List.of( + "con", + "-h", "localhost", + "-p", "22", + "-i", "cliTest" + ); + + mqttCliShell.executeCommand(connectCommand) + .awaitStdErr("Unable to connect.") + .awaitStdout("mqtt>"); } + } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java index 88db56880..8da321637 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,6 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; +import java.util.List; import java.util.concurrent.TimeUnit; public class DisconnectST { @@ -34,7 +35,7 @@ public class DisconnectST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -48,19 +49,18 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_disconnect() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithTimeout("dis", "mqtt>"); + void test_successful_disconnect() throws Exception { + final List disconnectCommand = List.of("dis"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeCommand(disconnectCommand).awaitStdout("mqtt>"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_unsuccessful_disconnect() { - cliShellTestExtension.executeCommandWithErrorWithTimeout( - "dis", - "Missing required option '--identifier='"); + void test_unsuccessful_disconnect() throws Exception { + final List disconnectCommand = List.of("dis"); + mqttCliShell.executeCommand(disconnectCommand) + .awaitStdErr("Missing required option '--identifier='") + .awaitStdout("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java index febbcfe58..e16eb1263 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,7 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; -import java.util.Set; +import java.util.List; import java.util.concurrent.TimeUnit; public class PublishST { @@ -35,7 +35,7 @@ public class PublishST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -49,39 +49,38 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_publish() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithTimeout("pub -t test -m test", "cliTest@" + hivemq.getHost() + ">"); + void test_successful_publish() throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeCommand(publishCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_topic() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithErrorWithTimeout("pub", "Missing required option: '--topic '"); + void test_publish_missing_topic() throws Exception { + final List publishCommand = List.of("pub"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeCommand(publishCommand) + .awaitStdErr("Missing required option: '--topic '") + .awaitStdout("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_message() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithErrorWithTimeout("pub -t test", Set.of( - "Error: Missing required argument (specify one of these): (-m | -m:file )", - "Error: Missing required argument (specify one of these): (-m:file | -m )")); + void test_publish_missing_message() throws Exception { + final List publishCommand = List.of("pub", "-t", "test"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeCommand(publishCommand) + .awaitStdErr("Error: Missing required argument (specify one of these)") + .awaitStdout("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_missing_arguments() { - cliShellTestExtension.executeCommandWithErrorWithTimeout("pub", "Unmatched argument at index 0: 'pub'"); + void test_missing_arguments() throws Exception { + final List publishCommand = List.of("pub"); + mqttCliShell.executeCommand(publishCommand) + .awaitStdErr("Unmatched argument at index 0: 'pub'") + .awaitStdout("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java index c9b1d9c04..179395cd2 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,6 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; +import java.util.List; import java.util.concurrent.TimeUnit; public class SubscribeST { @@ -34,7 +35,7 @@ public class SubscribeST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -48,29 +49,28 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_subscribe() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithTimeout("sub -t test", "cliTest@" + hivemq.getHost() + ">"); + void test_successful_subscribe() throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeCommand(subscribeCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_subscribe_missing_topic() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithErrorWithTimeout( - "sub", - "Missing required option: '--topic '"); + void test_subscribe_missing_topic() throws Exception{ + final List subscribeCommand = List.of("sub"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeCommand(subscribeCommand) + .awaitStdErr("Missing required option: '--topic '") + .awaitStdout("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_missing_arguments() { - cliShellTestExtension.executeCommandWithErrorWithTimeout("sub", "Unmatched argument at index 0: 'sub'"); + void test_missing_arguments() throws Exception { + final List subscribeCommand = List.of("sub"); + mqttCliShell.executeCommand(subscribeCommand) + .awaitStdErr("Unmatched argument at index 0: 'sub'") + .awaitStdout("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java new file mode 100644 index 000000000..88d3b84d5 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AwaitOutput { + + private final @NotNull ProcessIO processIO; + private final @NotNull String command; + + public AwaitOutput(final @NotNull ProcessIO processIO, final @NotNull String command) { + this.processIO = processIO; + this.command = command; + } + + public @NotNull AwaitOutput awaitStdout(final @NotNull String expectedOutput) { + final StringBuilder outputUntilStop = new StringBuilder(); + final CompletableFuture awaitMsg = processIO.awaitStdOutMessage(expectedOutput, outputUntilStop); + try { + assertEquals(true, awaitMsg.get(10, TimeUnit.SECONDS), + String.format("Expected command '%s' to produce output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); + } catch (final InterruptedException | ExecutionException exception) { + exception.printStackTrace(); + throw new RuntimeException(exception); + } catch (final TimeoutException timeoutException) { + Assertions.fail(String.format("Timeout: Expected command '%s' to produce output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); + } + return this; + } + + public @NotNull AwaitOutput awaitStdErr(final @NotNull String expectedOutput) { + final StringBuilder outputUntilStop = new StringBuilder(); + final CompletableFuture awaitMsg = processIO.awaitStdErrMessage(expectedOutput, outputUntilStop); + try { + assertEquals(true, awaitMsg.get(10, TimeUnit.SECONDS), + String.format("Expected command '%s' to produce error output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); + } catch (final InterruptedException | ExecutionException exception) { + exception.printStackTrace(); + throw new RuntimeException(exception); + } catch (final TimeoutException timeoutException) { + Assertions.fail(String.format("Timeout: Expected command '%s' to produce error output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); + } + return this; + } + public @NotNull AwaitOutput awaitLog(final @NotNull String expectedLogMessage) { + //FIXME + return null; + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java b/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java deleted file mode 100644 index a7a878d52..000000000 --- a/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.utils; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -public class CLIShellTestExtension implements BeforeEachCallback, AfterEachCallback { - - public static final @NotNull List CLI_EXEC = - Arrays.stream(Objects.requireNonNull(System.getProperty("cliExec")).split(" ")) - .collect(Collectors.toList()); - - private static final int TIMEOUT = 10; - - private @Nullable Process cliShell; - private final @NotNull CommandConsumer commandConsumer = new CommandConsumer(); - private final @NotNull CommandConsumer errorConsumer = new CommandConsumer(); - - @Override - public void beforeEach(final @NotNull ExtensionContext context) throws Exception { - final ArrayList shellCommand = new ArrayList<>(CLI_EXEC); - shellCommand.add("sh"); - cliShell = new ProcessBuilder(shellCommand).start(); - waitForStartup(cliShell).get(TIMEOUT, TimeUnit.SECONDS); - } - - @Override - public void afterEach(final @NotNull ExtensionContext context) { - if (cliShell == null) { - throw new IllegalStateException(); - } - cliShell.destroy(); - } - - private @NotNull CompletableFuture waitForStartup(final @NotNull Process cliShell) { - final InputStream inputStream = cliShell.getInputStream(); - final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - final BufferedReader bufferedInputReader = new BufferedReader(inputStreamReader); - - final CompletableFuture cliReady = commandConsumer.waitFor("mqtt>"); - final StringBuilder commandBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (!cliReady.isDone()) { - try { - final int inputChar; - inputChar = bufferedInputReader.read(); - - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but command was not found."); - } - commandBuilder.append((char) inputChar); - commandConsumer.accept(commandBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } - - public void executeCommandWithTimeout(final @NotNull String command, final @NotNull String expectedReturn) { - executeCommandWithTimeout(command, Set.of(expectedReturn)); - } - - public @NotNull CompletableFuture executeCommand( - final @NotNull String command, final @NotNull String expectedReturn) { - return executeCommand(command, Set.of(expectedReturn)); - } - - public void executeCommandWithTimeout(final @NotNull String command, final @NotNull Set expectedReturns) { - try { - executeCommand(command, expectedReturns).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture executeCommand( - final @NotNull String command, final @NotNull Set expectedReturns) { - if (cliShell == null) { - throw new IllegalStateException(); - } - - try { - final OutputStream outputStream = cliShell.getOutputStream(); - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); - final BufferedWriter bufferedOutputWriter = new BufferedWriter(outputStreamWriter); - bufferedOutputWriter.write(command); - bufferedOutputWriter.write("\n"); - bufferedOutputWriter.flush(); - } catch (final IOException e) { - throw new RuntimeException(e); - } - - final InputStream inputStream = cliShell.getInputStream(); - final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - final BufferedReader bufferedInputReader = new BufferedReader(inputStreamReader); - - final ArrayList> commandFinished = new ArrayList<>(); - for (final String expectedReturn : expectedReturns) { - commandFinished.add(commandConsumer.waitFor(expectedReturn)); - } - final StringBuilder commandBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (commandFinished.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { - try { - final int inputChar; - inputChar = bufferedInputReader.read(); - - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but command was not found."); - } - commandBuilder.append((char) inputChar); - commandConsumer.accept(commandBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } - - public void executeCommandWithErrorWithTimeout( - final @NotNull String command, final @NotNull String expectedError) { - executeCommandWithErrorWithTimeout(command, Set.of(expectedError)); - } - - public @NotNull CompletableFuture executeCommandWithError( - final @NotNull String command, final @NotNull String expectedError) { - return executeCommandWithError(command, Set.of(expectedError)); - } - - public void executeCommandWithErrorWithTimeout( - final @NotNull String command, final @NotNull Set expectedErrors) { - try { - executeCommandWithError(command, expectedErrors).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture executeCommandWithError( - final @NotNull String command, final @NotNull Set expectedErrors) { - if (cliShell == null) { - throw new IllegalStateException(); - } - - try { - final OutputStream outputStream = cliShell.getOutputStream(); - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); - final BufferedWriter bufferedOutputWriter = new BufferedWriter(outputStreamWriter); - bufferedOutputWriter.write(command); - bufferedOutputWriter.write("\n"); - bufferedOutputWriter.flush(); - } catch (final IOException e) { - throw new RuntimeException(e); - } - - final InputStream errorStream = cliShell.getErrorStream(); - final InputStreamReader errorStreamReader = new InputStreamReader(errorStream, StandardCharsets.UTF_8); - final BufferedReader bufferedErrorReader = new BufferedReader(errorStreamReader); - - final ArrayList> commandFinished = new ArrayList<>(); - for (final String expectedError : expectedErrors) { - commandFinished.add(errorConsumer.waitFor(expectedError)); - } - final StringBuilder errorBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (commandFinished.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { - try { - final int inputChar = bufferedErrorReader.read(); - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but error was not found."); - } - errorBuilder.append((char) inputChar); - errorConsumer.accept(errorBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } -} diff --git a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java b/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java deleted file mode 100644 index e467fafd2..000000000 --- a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.utils; - -import org.jetbrains.annotations.NotNull; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -public class CLITestExtension { - - public static final @NotNull List CLI_EXEC = - Arrays.stream(Objects.requireNonNull(System.getProperty("cliExec")).split(" ")) - .collect(Collectors.toList()); - - private static final int TIMEOUT = 10; - - private final @NotNull CommandConsumer commandConsumer = new CommandConsumer(); - private final @NotNull CommandConsumer errorConsumer = new CommandConsumer(); - - public void waitForOutputWithTimeout(final @NotNull Process process, final @NotNull String expectedReturn) { - try { - waitForOutput(process, expectedReturn).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture waitForOutput( - final @NotNull Process process, final @NotNull String expectedReturn) { - final InputStream inputStream = process.getInputStream(); - final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - final BufferedReader bufferedInputReader = new BufferedReader(inputStreamReader); - - final CompletableFuture commandReturned = commandConsumer.waitFor(expectedReturn); - final StringBuilder commandBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (!commandReturned.isDone()) { - try { - final int inputChar; - inputChar = bufferedInputReader.read(); - - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but command was not found."); - } - commandBuilder.append((char) inputChar); - commandConsumer.accept(commandBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } - - public void waitForErrorWithTimeout( - final @NotNull Process process, final @NotNull String expectedError) { - waitForErrorWithTimeout(process, Set.of(expectedError)); - } - - public void waitForErrorWithTimeout( - final @NotNull Process process, final @NotNull Set expectedErrors) { - try { - waitForError(process, expectedErrors).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture waitForError( - final @NotNull Process process, final @NotNull Set expectedErrors) { - final InputStream errorStream = process.getErrorStream(); - final InputStreamReader errorStreamReader = new InputStreamReader(errorStream, StandardCharsets.UTF_8); - final BufferedReader bufferedErrorReader = new BufferedReader(errorStreamReader); - - final ArrayList> errorReadFutures = new ArrayList<>(); - for (final String expectedError : expectedErrors) { - errorReadFutures.add(errorConsumer.waitFor(expectedError)); - } - final StringBuilder errorBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (errorReadFutures.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { - try { - final int inputChar = bufferedErrorReader.read(); - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but error was not found."); - } - errorBuilder.append((char) inputChar); - errorConsumer.accept(errorBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } -} diff --git a/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java b/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java deleted file mode 100644 index 7a85b12e8..000000000 --- a/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.utils; - -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; - -public class CommandConsumer implements Consumer { - - private final @NotNull Map> contains = new ConcurrentHashMap<>(); - - @Override - public void accept(final @NotNull String commandLine) { - contains.forEach((command, future) -> { - if (commandLine.contains(command)) { - future.complete(null); - } - }); - } - - public @NotNull CompletableFuture waitFor(final @NotNull String command) { - final CompletableFuture future = new CompletableFuture<>(); - contains.put(command, future); - return future; - } -} \ No newline at end of file diff --git a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java new file mode 100644 index 000000000..bdb4f27a2 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; + +public class ExecutionResult { + private final int exitCode; + private final @NotNull String standardOutput; + private final @NotNull String errorOutput; + + public ExecutionResult(final int exitCode, final @NotNull String standardOutput, final @NotNull String errorOutput) { + this.exitCode = exitCode; + this.standardOutput = standardOutput; + this.errorOutput = errorOutput; + } + + public int getExitCode() { + return exitCode; + } + + public @NotNull String getStandardOutput() { + return standardOutput; + } + + public @NotNull String getErrorOutput() { + return errorOutput; + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java new file mode 100644 index 000000000..f67d1a4e8 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MqttCli { + + // See 'systemTest' and 'systemTestNative' in build.gradle.kts + // Depending on the task used, the property cliExec either contains the absolute path to the jar or the native image executable + public static final @NotNull List CLI_EXEC = + Arrays.stream(Objects.requireNonNull(System.getProperty("cliExec")).split(" ")) + .collect(Collectors.toUnmodifiableList()); + + public @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { + final List fullCommand = new ArrayList<>(CLI_EXEC); + assertTrue(fullCommand.addAll(command)); + + final Process process = new ProcessBuilder(fullCommand).start(); + + final int exitCode = process.waitFor(); + + final String stdOut = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + final String stdErr = new String(process.getErrorStream().readAllBytes(), StandardCharsets.UTF_8); + + return new ExecutionResult(exitCode, stdOut, stdErr); + } + + public @NotNull AwaitOutput executeAsync(final @NotNull List command) + throws IOException { + final List fullCommand = new ArrayList<>(CLI_EXEC); + assertTrue(fullCommand.addAll(command)); + + final Process process = new ProcessBuilder(fullCommand).start(); + final ProcessIO processIO = ProcessIO.startReading(process); + + return new AwaitOutput(processIO, String.join(" ", command)); + } + +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java new file mode 100644 index 000000000..c2a5148ac --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { + + private ProcessIO processIO; + private Process process; + + @Override + public void beforeEach(final @NotNull ExtensionContext context) throws Exception { + startShellMode(); + } + + @Override + public void afterEach(final @NotNull ExtensionContext context) { + if (process.isAlive()) { + process.destroy(); + } + } + + private void startShellMode() throws IOException { + final List shellCommand = new ArrayList<>(CLI_EXEC); + assertTrue(shellCommand.addAll(List.of("sh", "-l"))); + + this.process = new ProcessBuilder(shellCommand).start(); + this.processIO = ProcessIO.startReading(process); + + final AwaitOutput awaitOutput = new AwaitOutput(processIO, String.join(" ", shellCommand)); + awaitOutput.awaitStdout("mqtt>"); + } + + public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) throws Exception { + final List connectCommand = + List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-i", "cliTest"); + + executeCommand(connectCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); + } + + public @NotNull AwaitOutput executeCommand(final @NotNull List command) throws IOException { + final String fullCommand = String.join(" ", command); + processIO.writeMsg(fullCommand); + return new AwaitOutput(processIO, fullCommand); + } + + +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java new file mode 100644 index 000000000..5c9bf9da5 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java @@ -0,0 +1,156 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.apache.commons.io.input.TeeInputStream; +import org.apache.commons.io.output.TeeOutputStream; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; + +public class ProcessIO { + + private final @NotNull BufferedReader reader; + private final @NotNull BufferedReader readerErr; + private final @NotNull BufferedWriter writer; + private final @NotNull PrintStream stdout; + + private ProcessIO( + final @NotNull BufferedReader reader, + final @NotNull BufferedReader readerErr, + final @NotNull BufferedWriter writer, + final @NotNull PrintStream stdout) { + this.reader = reader; + this.readerErr = readerErr; + this.writer = writer; + this.stdout = stdout; + } + + public static @NotNull ProcessIO startReading(final @NotNull Process process) throws IOException { + + final PipedInputStream pipeIn = new PipedInputStream(); + final PipedOutputStream pipeOut = new PipedOutputStream(pipeIn); + final BufferedReader reader = new BufferedReader(new InputStreamReader(pipeIn, StandardCharsets.UTF_8)); + final BufferedWriter writer = + new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8)); + final PrintStream stdout = new PrintStream(System.out, true, StandardCharsets.UTF_8); + final TeeOutputStream teeOut = new TeeOutputStream(pipeOut, stdout); + final TeeInputStream teeInStdOut = new TeeInputStream(process.getInputStream(), teeOut); + + CompletableFuture.runAsync(() -> { + try { + int previousChar = '\0'; + while (true) { + final int readChar = teeInStdOut.read(); + + if (readChar == -1) { + return; + } + + if (previousChar == '>' && readChar == ' ') { + stdout.flush(); + } + + previousChar = readChar; + } + } catch (final Exception ex) { + ex.printStackTrace(); + } + + }); + + + final PipedInputStream pipeInErr = new PipedInputStream(); + final PipedOutputStream pipeOutErr = new PipedOutputStream(pipeInErr); + final BufferedReader readerErr = new BufferedReader(new InputStreamReader(pipeInErr, StandardCharsets.UTF_8)); + final PrintStream stdErr = new PrintStream(System.err, true, StandardCharsets.UTF_8); + final TeeOutputStream teeOutErr = new TeeOutputStream(pipeOutErr, stdErr); + final TeeInputStream teeInStdErr = new TeeInputStream(process.getErrorStream(), teeOutErr); + + CompletableFuture.runAsync(() -> { + try { + while (true) { + final int readChar = teeInStdErr.read(); + + if (readChar == -1) { + return; + } + + } + } catch (final Exception ex) { + ex.printStackTrace(); + } + }); + + return new ProcessIO(reader, readerErr, writer, stdout); + } + + + public @NotNull CompletableFuture awaitStdOutMessage(final @NotNull String message, final @NotNull StringBuilder outputUntilStop) { + return awaitMessage(reader, message, outputUntilStop); + } + + public @NotNull CompletableFuture awaitStdErrMessage(final @NotNull String message, final @NotNull StringBuilder outputUntilStop) { + return awaitMessage(readerErr, message, outputUntilStop); + } + + private CompletableFuture awaitMessage(final @NotNull BufferedReader reader, final @NotNull String message, final @NotNull StringBuilder outputUntilStop) { + return CompletableFuture.supplyAsync(() -> { + while (true) { + try { + for (int i = 0; i < message.length(); i++) { + + // Read next character from the input stream + final int readChar = reader.read(); + + // End of stream reached + if (readChar == -1) { + return false; + } + + outputUntilStop.append((char) readChar); + + // Start new iteration + if (readChar != message.charAt(i)) { + break; + } + + // Message is contained + if ((i == message.length() - 1)) { + return true; + } + } + } catch (final IOException ex) { + ex.printStackTrace(); + return false; + } + } + }); + + } + + public void writeMsg(final @NotNull String message) throws IOException { + stdout.write(message.getBytes(StandardCharsets.UTF_8)); + stdout.write('\n'); + + writer.write(message); + writer.write("\n"); + writer.flush(); + } + +} From 64b82607f2c938e404cdc39ecef96073d3373bcb Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Fri, 30 Sep 2022 18:27:06 +0200 Subject: [PATCH 15/64] WIP: gh-action > try out reporting --- .github/workflows/check.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 49b8a812d..896cf2562 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -26,3 +26,8 @@ jobs: run: ./gradlew installNativeImageTooling - name: Check run: ./gradlew check --stacktrace + - name: Publish Test Report + uses: mikepenz/action-junit-report@v3 + if: always() # always run even if the previous step fails + with: + report_paths: '**/build/test-results/*/TEST-*.xml' From 64a42e841cef9b7a1fe78146a357ee4b77fd5372 Mon Sep 17 00:00:00 2001 From: gitseti Date: Fri, 30 Sep 2022 18:45:31 +0200 Subject: [PATCH 16/64] WIP: System Tests > Add debug assertion --- src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java index 5c9bf9da5..904145c60 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java @@ -18,6 +18,7 @@ import org.apache.commons.io.input.TeeInputStream; import org.apache.commons.io.output.TeeOutputStream; import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; import java.io.*; import java.nio.charset.StandardCharsets; @@ -71,7 +72,6 @@ private ProcessIO( } catch (final Exception ex) { ex.printStackTrace(); } - }); @@ -120,6 +120,7 @@ private CompletableFuture awaitMessage(final @NotNull BufferedReader re // End of stream reached if (readChar == -1) { + Assertions.fail("Reached end of stream."); return false; } @@ -136,6 +137,7 @@ private CompletableFuture awaitMessage(final @NotNull BufferedReader re } } } catch (final IOException ex) { + Assertions.fail(ex); ex.printStackTrace(); return false; } From 5e14f840efd148d3adb42788ba3e2d3362cd3228 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Fri, 30 Sep 2022 23:55:58 +0200 Subject: [PATCH 17/64] WIP: System Tests > Upload test results --- .github/workflows/check.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 896cf2562..7df8802f2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -31,3 +31,7 @@ jobs: if: always() # always run even if the previous step fails with: report_paths: '**/build/test-results/*/TEST-*.xml' + - uses: actions/upload-artifact@v3 + with: + name: test-results + path: './build/test-results/' From 1677e861f87ecbe406ec434ec2d65dce23129be0 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Sat, 1 Oct 2022 00:06:20 +0200 Subject: [PATCH 18/64] WIP: System Tests > Add name for action --- .github/workflows/check.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7df8802f2..bbcd5a649 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -31,7 +31,8 @@ jobs: if: always() # always run even if the previous step fails with: report_paths: '**/build/test-results/*/TEST-*.xml' - - uses: actions/upload-artifact@v3 + - name: Upload Test Results + uses: actions/upload-artifact@v3 with: name: test-results path: './build/test-results/' From 68000b610cd03c5af009aeb74af220a1a6e5f23a Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Sat, 1 Oct 2022 00:08:12 +0200 Subject: [PATCH 19/64] WIP: System Tests > Fix path --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index bbcd5a649..46b8c587b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -35,4 +35,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: test-results - path: './build/test-results/' + path: build/test-results/ From 2c574dbf643d7be2d1753bef419da67a10e2584e Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Sat, 1 Oct 2022 00:25:08 +0200 Subject: [PATCH 20/64] WIP: System Tests > Add if failure --- .github/workflows/check.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 46b8c587b..560ce4904 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -26,13 +26,15 @@ jobs: run: ./gradlew installNativeImageTooling - name: Check run: ./gradlew check --stacktrace - - name: Publish Test Report - uses: mikepenz/action-junit-report@v3 - if: always() # always run even if the previous step fails - with: - report_paths: '**/build/test-results/*/TEST-*.xml' - name: Upload Test Results uses: actions/upload-artifact@v3 + if: failure() with: name: test-results path: build/test-results/ + - name: Publish Test Report + uses: mikepenz/action-junit-report@v3 + if: always() # always run even if the previous step fails + with: + report_paths: '**/build/test-results/*/TEST-*.xml' + From 5613be71c74aeff0446864e341e1b9fa192f6ec3 Mon Sep 17 00:00:00 2001 From: gitseti Date: Sat, 1 Oct 2022 01:28:23 +0200 Subject: [PATCH 21/64] Make Ansi coloring automatic --- src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java b/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java index e08cd44eb..fb1e39acd 100644 --- a/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java +++ b/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java @@ -27,7 +27,7 @@ public class CommandLineConfig { //@formatter:off private final static @NotNull CommandLine.Help.ColorScheme COLOR_SCHEME = - new CommandLine.Help.ColorScheme.Builder(CommandLine.Help.Ansi.ON) + new CommandLine.Help.ColorScheme.Builder(CommandLine.Help.Ansi.AUTO) .commands(CommandLine.Help.Ansi.Style.bold, CommandLine.Help.Ansi.Style.fg_yellow) .options(CommandLine.Help.Ansi.Style.italic, CommandLine.Help.Ansi.Style.fg_yellow) .parameters(CommandLine.Help.Ansi.Style.fg_yellow) From 318ca24dee35d3f2e4861d717379bff7eaac467a Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 4 Oct 2022 15:02:20 +0200 Subject: [PATCH 22/64] System Tests > Refactor test api --- .../hivemq/cli/commands/cli/SubscribeST.java | 4 +- .../cli/commands/cli/shell/ConnectST.java | 6 +- .../cli/commands/cli/shell/DisconnectST.java | 6 +- .../cli/commands/cli/shell/PublishST.java | 14 +- .../cli/commands/cli/shell/SubscribeST.java | 12 +- .../com/hivemq/cli/utils/AwaitOutput.java | 30 +--- .../com/hivemq/cli/utils/MqttCliShell.java | 10 +- .../java/com/hivemq/cli/utils/ProcessIO.java | 168 ++++++++++-------- .../hivemq/cli/utils/TimeoutException.java | 25 +++ 9 files changed, 147 insertions(+), 128 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 808b9b48b..86319b989 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -73,11 +73,11 @@ void test_successful_subscribe() throws Exception { final AwaitOutput awaitOutput = mqttCli.executeAsync(subscribeCommand); - awaitOutput.awaitStdout("received SUBACK"); + awaitOutput.awaitStdOut("received SUBACK"); publisher.publishWith().topic("test").payload("testReturn".getBytes(StandardCharsets.UTF_8)).send(); - awaitOutput.awaitStdout("testReturn"); + awaitOutput.awaitStdOut("testReturn"); } @Test diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java index 31f8e84d2..ccd040967 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java @@ -57,7 +57,7 @@ void test_successful_connect() throws Exception { "-i", "cliTest" ); - mqttCliShell.executeCommand(connectCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } @@ -71,9 +71,9 @@ void test_unsuccessful_connect() throws Exception { "-i", "cliTest" ); - mqttCliShell.executeCommand(connectCommand) + mqttCliShell.executeAsync(connectCommand) .awaitStdErr("Unable to connect.") - .awaitStdout("mqtt>"); + .awaitStdOut("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java index 8da321637..d10a3b24e 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java @@ -52,15 +52,15 @@ static void afterAll() { void test_successful_disconnect() throws Exception { final List disconnectCommand = List.of("dis"); mqttCliShell.connectClient(hivemq); - mqttCliShell.executeCommand(disconnectCommand).awaitStdout("mqtt>"); + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_unsuccessful_disconnect() throws Exception { final List disconnectCommand = List.of("dis"); - mqttCliShell.executeCommand(disconnectCommand) + mqttCliShell.executeAsync(disconnectCommand) .awaitStdErr("Missing required option '--identifier='") - .awaitStdout("mqtt>"); + .awaitStdOut("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java index e16eb1263..3fb742830 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java @@ -52,7 +52,7 @@ static void afterAll() { void test_successful_publish() throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test"); mqttCliShell.connectClient(hivemq); - mqttCliShell.executeCommand(publishCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(publishCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } @Test @@ -60,9 +60,9 @@ void test_successful_publish() throws Exception { void test_publish_missing_topic() throws Exception { final List publishCommand = List.of("pub"); mqttCliShell.connectClient(hivemq); - mqttCliShell.executeCommand(publishCommand) + mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Missing required option: '--topic '") - .awaitStdout("cliTest@" + hivemq.getHost() + ">"); + .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); } @Test @@ -70,17 +70,17 @@ void test_publish_missing_topic() throws Exception { void test_publish_missing_message() throws Exception { final List publishCommand = List.of("pub", "-t", "test"); mqttCliShell.connectClient(hivemq); - mqttCliShell.executeCommand(publishCommand) + mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Error: Missing required argument (specify one of these)") - .awaitStdout("cliTest@" + hivemq.getHost() + ">"); + .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_missing_arguments() throws Exception { final List publishCommand = List.of("pub"); - mqttCliShell.executeCommand(publishCommand) + mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Unmatched argument at index 0: 'pub'") - .awaitStdout("mqtt>"); + .awaitStdOut("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java index 179395cd2..314995e01 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java @@ -52,7 +52,7 @@ static void afterAll() { void test_successful_subscribe() throws Exception { final List subscribeCommand = List.of("sub", "-t", "test"); mqttCliShell.connectClient(hivemq); - mqttCliShell.executeCommand(subscribeCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(subscribeCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } @Test @@ -60,17 +60,17 @@ void test_successful_subscribe() throws Exception { void test_subscribe_missing_topic() throws Exception{ final List subscribeCommand = List.of("sub"); mqttCliShell.connectClient(hivemq); - mqttCliShell.executeCommand(subscribeCommand) + mqttCliShell.executeAsync(subscribeCommand) .awaitStdErr("Missing required option: '--topic '") - .awaitStdout("cliTest@" + hivemq.getHost() + ">"); + .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_missing_arguments() throws Exception { final List subscribeCommand = List.of("sub"); - mqttCliShell.executeCommand(subscribeCommand) - .awaitStdErr("Unmatched argument at index 0: 'sub'") - .awaitStdout("mqtt>"); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut("mqtt>") + .awaitStdErr("Unmatched argument at index 0: 'sub'"); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java index 88d3b84d5..deb79b382 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java +++ b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java @@ -18,13 +18,6 @@ import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.junit.jupiter.api.Assertions.assertEquals; - public class AwaitOutput { private final @NotNull ProcessIO processIO; @@ -35,35 +28,24 @@ public AwaitOutput(final @NotNull ProcessIO processIO, final @NotNull String com this.command = command; } - public @NotNull AwaitOutput awaitStdout(final @NotNull String expectedOutput) { - final StringBuilder outputUntilStop = new StringBuilder(); - final CompletableFuture awaitMsg = processIO.awaitStdOutMessage(expectedOutput, outputUntilStop); + public @NotNull AwaitOutput awaitStdOut(final @NotNull String expectedOutput) { try { - assertEquals(true, awaitMsg.get(10, TimeUnit.SECONDS), - String.format("Expected command '%s' to produce output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); - } catch (final InterruptedException | ExecutionException exception) { - exception.printStackTrace(); - throw new RuntimeException(exception); + processIO.awaitStdOut(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Timeout: Expected command '%s' to produce output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); + Assertions.fail(String.format("Command '%s' did not return expected standard output '%s' in time.", command, expectedOutput), timeoutException); } return this; } public @NotNull AwaitOutput awaitStdErr(final @NotNull String expectedOutput) { - final StringBuilder outputUntilStop = new StringBuilder(); - final CompletableFuture awaitMsg = processIO.awaitStdErrMessage(expectedOutput, outputUntilStop); try { - assertEquals(true, awaitMsg.get(10, TimeUnit.SECONDS), - String.format("Expected command '%s' to produce error output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); - } catch (final InterruptedException | ExecutionException exception) { - exception.printStackTrace(); - throw new RuntimeException(exception); + processIO.awaitStdErr(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Timeout: Expected command '%s' to produce error output '%s' but only read '%s'", command, expectedOutput, outputUntilStop)); + Assertions.fail(String.format("Command '%s' did not return expected error output '%s' in time.", command, expectedOutput), timeoutException); } return this; } + public @NotNull AwaitOutput awaitLog(final @NotNull String expectedLogMessage) { //FIXME return null; diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index c2a5148ac..eed72b5e0 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -47,24 +47,24 @@ public void afterEach(final @NotNull ExtensionContext context) { private void startShellMode() throws IOException { final List shellCommand = new ArrayList<>(CLI_EXEC); - assertTrue(shellCommand.addAll(List.of("sh", "-l"))); + assertTrue(shellCommand.addAll(List.of("sh"))); this.process = new ProcessBuilder(shellCommand).start(); this.processIO = ProcessIO.startReading(process); - final AwaitOutput awaitOutput = new AwaitOutput(processIO, String.join(" ", shellCommand)); - awaitOutput.awaitStdout("mqtt>"); + new AwaitOutput(processIO, String.join(" ", shellCommand)).awaitStdOut("mqtt>"); } public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) throws Exception { final List connectCommand = List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-i", "cliTest"); - executeCommand(connectCommand).awaitStdout(String.format("cliTest@%s>", hivemq.getHost())); + executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } - public @NotNull AwaitOutput executeCommand(final @NotNull List command) throws IOException { + public @NotNull AwaitOutput executeAsync(final @NotNull List command) throws IOException { final String fullCommand = String.join(" ", command); + processIO.writeMsg(fullCommand); return new AwaitOutput(processIO, fullCommand); } diff --git a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java index 904145c60..364542d8f 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java @@ -15,56 +15,67 @@ */ package com.hivemq.cli.utils; -import org.apache.commons.io.input.TeeInputStream; -import org.apache.commons.io.output.TeeOutputStream; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Assertions; +import org.testcontainers.shaded.org.awaitility.core.ConditionTimeoutException; -import java.io.*; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; public class ProcessIO { - private final @NotNull BufferedReader reader; - private final @NotNull BufferedReader readerErr; - private final @NotNull BufferedWriter writer; - private final @NotNull PrintStream stdout; + private final @NotNull BufferedWriter stdoutWriter; + private final @NotNull StringBuilder processStdOut; + private final @NotNull StringBuilder processStdErr; + private final @NotNull BufferedWriter processOutputWriter; + private final @NotNull AtomicInteger processStdOutMarker; + private final @NotNull AtomicInteger processStdErrMarker; private ProcessIO( - final @NotNull BufferedReader reader, - final @NotNull BufferedReader readerErr, - final @NotNull BufferedWriter writer, - final @NotNull PrintStream stdout) { - this.reader = reader; - this.readerErr = readerErr; - this.writer = writer; - this.stdout = stdout; + final @NotNull BufferedWriter stdoutWriter, + final @NotNull StringBuilder processStdOut, + final @NotNull StringBuilder processStdErr, + final @NotNull BufferedWriter processOutputWriter) { + this.stdoutWriter = stdoutWriter; + this.processStdOut = processStdOut; + this.processStdErr = processStdErr; + this.processOutputWriter = processOutputWriter; + this.processStdOutMarker = new AtomicInteger(0); + this.processStdErrMarker = new AtomicInteger(0); } - public static @NotNull ProcessIO startReading(final @NotNull Process process) throws IOException { + public static @NotNull ProcessIO startReading(final @NotNull Process process) { + + final StringBuilder processStdOut = new StringBuilder(); + final StringBuilder processErrOut = new StringBuilder(); + final BufferedWriter processOutputWriter = + new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); - final PipedInputStream pipeIn = new PipedInputStream(); - final PipedOutputStream pipeOut = new PipedOutputStream(pipeIn); - final BufferedReader reader = new BufferedReader(new InputStreamReader(pipeIn, StandardCharsets.UTF_8)); - final BufferedWriter writer = - new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8)); - final PrintStream stdout = new PrintStream(System.out, true, StandardCharsets.UTF_8); - final TeeOutputStream teeOut = new TeeOutputStream(pipeOut, stdout); - final TeeInputStream teeInStdOut = new TeeInputStream(process.getInputStream(), teeOut); + /* Startup std-out writer */ + final InputStream processInputStream = process.getInputStream(); + final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)); CompletableFuture.runAsync(() -> { try { int previousChar = '\0'; while (true) { - final int readChar = teeInStdOut.read(); + final int readChar = processInputStream.read(); if (readChar == -1) { return; } - if (previousChar == '>' && readChar == ' ') { - stdout.flush(); + writer.write(readChar); + processStdOut.append((char) readChar); + + if ((previousChar == '>' && readChar == ' ') || readChar == '\n') { + writer.flush(); } previousChar = readChar; @@ -74,85 +85,86 @@ private ProcessIO( } }); - - final PipedInputStream pipeInErr = new PipedInputStream(); - final PipedOutputStream pipeOutErr = new PipedOutputStream(pipeInErr); - final BufferedReader readerErr = new BufferedReader(new InputStreamReader(pipeInErr, StandardCharsets.UTF_8)); - final PrintStream stdErr = new PrintStream(System.err, true, StandardCharsets.UTF_8); - final TeeOutputStream teeOutErr = new TeeOutputStream(pipeOutErr, stdErr); - final TeeInputStream teeInStdErr = new TeeInputStream(process.getErrorStream(), teeOutErr); + /* Startup std-err writer */ + final InputStream processErrorStream = process.getErrorStream(); + final BufferedWriter errorWriter = + new BufferedWriter(new OutputStreamWriter(System.err, StandardCharsets.UTF_8)); CompletableFuture.runAsync(() -> { try { while (true) { - final int readChar = teeInStdErr.read(); + final int readChar = processErrorStream.read(); if (readChar == -1) { return; } + errorWriter.write(readChar); + processErrOut.append((char) readChar); + + if (readChar == '\n') { + errorWriter.flush(); + } } } catch (final Exception ex) { ex.printStackTrace(); } }); - return new ProcessIO(reader, readerErr, writer, stdout); + return new ProcessIO(writer, processStdOut, processErrOut, processOutputWriter); } + public void awaitStdOut(final @NotNull String stdOutMessage) throws TimeoutException { + try { + await().until(() -> { + final int index = processStdOut.indexOf(stdOutMessage, processStdOutMarker.get()); - public @NotNull CompletableFuture awaitStdOutMessage(final @NotNull String message, final @NotNull StringBuilder outputUntilStop) { - return awaitMessage(reader, message, outputUntilStop); - } + if (index == -1) { + return false; + } - public @NotNull CompletableFuture awaitStdErrMessage(final @NotNull String message, final @NotNull StringBuilder outputUntilStop) { - return awaitMessage(readerErr, message, outputUntilStop); + processStdOutMarker.set(index + stdOutMessage.length()); + return true; + }); + } catch (final @NotNull ConditionTimeoutException timeoutException) { + final String errorMessage = String.format( + "Timeout while waiting for expected standard output '%s'. Actual: '%s'", + stdOutMessage, + processStdOut.substring(processStdErrMarker.get())); + throw new TimeoutException(errorMessage, timeoutException); + } } - private CompletableFuture awaitMessage(final @NotNull BufferedReader reader, final @NotNull String message, final @NotNull StringBuilder outputUntilStop) { - return CompletableFuture.supplyAsync(() -> { - while (true) { - try { - for (int i = 0; i < message.length(); i++) { + public void awaitStdErr(final @NotNull String stdErrMessage) throws TimeoutException { + try { + await().until(() -> { + final int index = processStdErr.indexOf(stdErrMessage, processStdErrMarker.get()); - // Read next character from the input stream - final int readChar = reader.read(); - - // End of stream reached - if (readChar == -1) { - Assertions.fail("Reached end of stream."); - return false; - } - - outputUntilStop.append((char) readChar); - - // Start new iteration - if (readChar != message.charAt(i)) { - break; - } - - // Message is contained - if ((i == message.length() - 1)) { - return true; - } - } - } catch (final IOException ex) { - Assertions.fail(ex); - ex.printStackTrace(); + if (index == -1) { return false; } - } - }); + + processStdErrMarker.set(index + stdErrMessage.length()); + return true; + }); + } catch (final ConditionTimeoutException timeoutException) { + final String errorMessage = String.format( + "Timeout while waiting for expected error output '%s'. Actual: '%s'", + stdErrMessage, + processStdErr.substring(processStdErrMarker.get())); + throw new TimeoutException(errorMessage, timeoutException); + } } public void writeMsg(final @NotNull String message) throws IOException { - stdout.write(message.getBytes(StandardCharsets.UTF_8)); - stdout.write('\n'); + stdoutWriter.write(message); + stdoutWriter.write('\n'); + stdoutWriter.flush(); - writer.write(message); - writer.write("\n"); - writer.flush(); + processOutputWriter.write(message); + processOutputWriter.write('\n'); + processOutputWriter.flush(); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java b/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java new file mode 100644 index 000000000..af8f8fd49 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; + +public class TimeoutException extends Exception { + + public TimeoutException(final @NotNull String message, final @NotNull Throwable cause) { + super(message, cause); + } +} From 3aca15085db41826ddd8bff568e55d60a409005d Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Fri, 14 Oct 2022 14:53:40 +0200 Subject: [PATCH 23/64] Refactored system tests. --- .editorconfig | 1161 +++++++++++++++++ .idea/codeStyles/Project.xml | 324 ++++- .idea/inspectionProfiles/Project_Default.xml | 2 +- .../hivemq/cli/commands/cli/PublishST.java | 52 +- .../hivemq/cli/commands/cli/SubscribeST.java | 19 +- .../cli/commands/cli/shell/ConnectST.java | 26 +- .../cli/commands/cli/shell/DisconnectST.java | 5 +- .../cli/commands/cli/shell/PublishST.java | 3 +- .../cli/commands/cli/shell/SubscribeST.java | 5 +- .../com/hivemq/cli/utils/AwaitOutput.java | 8 +- .../com/hivemq/cli/utils/ExecutionResult.java | 3 +- .../java/com/hivemq/cli/utils/MqttCli.java | 7 +- .../com/hivemq/cli/utils/MqttCliShell.java | 14 +- .../java/com/hivemq/cli/utils/ProcessIO.java | 4 +- .../hivemq/cli/utils/TimeoutException.java | 4 +- 15 files changed, 1536 insertions(+), 101 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..491cde9d3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,1161 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_visual_guides = none +ij_wrap_on_typing = false + +[*.css] +ij_css_align_closing_brace_with_properties = false +ij_css_blank_lines_around_nested_selector = 1 +ij_css_blank_lines_between_blocks = 1 +ij_css_block_comment_add_space = false +ij_css_brace_placement = end_of_line +ij_css_enforce_quotes_on_format = false +ij_css_hex_color_long_format = false +ij_css_hex_color_lower_case = false +ij_css_hex_color_short_format = false +ij_css_hex_color_upper_case = false +ij_css_keep_blank_lines_in_code = 2 +ij_css_keep_indents_on_empty_lines = false +ij_css_keep_single_line_blocks = false +ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow +ij_css_space_after_colon = true +ij_css_space_before_opening_brace = true +ij_css_use_double_quotes = true +ij_css_value_alignment = do_not_align + +[*.feature] +indent_size = 2 +ij_gherkin_keep_indents_on_empty_lines = false + +[*.haml] +indent_size = 2 +ij_haml_keep_indents_on_empty_lines = false + +[*.java] +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = true +ij_java_align_multiline_array_initializer_expression = true +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = false +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = true +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = true +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_records = true +ij_java_align_multiline_resources = true +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_text_blocks = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = false +ij_java_align_types_in_multi_catch = true +ij_java_annotation_parameter_wrap = on_every_item +ij_java_array_initializer_new_line_after_left_brace = true +ij_java_array_initializer_right_brace_on_new_line = false +ij_java_array_initializer_wrap = on_every_item +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = off +ij_java_assignment_wrap = normal +ij_java_binary_operation_sign_on_next_line = false +ij_java_binary_operation_wrap = on_every_item +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_before_imports = 1 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 0 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_add_space = false +ij_java_block_comment_at_first_column = true +ij_java_builder_methods = none +ij_java_call_parameters_new_line_after_left_paren = false +ij_java_call_parameters_right_paren_on_new_line = false +ij_java_call_parameters_wrap = on_every_item +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 50 +ij_java_class_names_in_javadoc = 1 +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_not_wrap_after_single_annotation_in_parameter = false +ij_java_do_while_brace_force = always +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = true +ij_java_doc_align_param_comments = true +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = true +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = true +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_entity_dd_suffix = EJB +ij_java_entity_eb_suffix = Bean +ij_java_entity_hi_suffix = Home +ij_java_entity_lhi_prefix = Local +ij_java_entity_lhi_suffix = Home +ij_java_entity_li_prefix = Local +ij_java_entity_pk_class = java.lang.String +ij_java_entity_vo_suffix = VO +ij_java_enum_constants_wrap = split_into_lines +ij_java_extends_keyword_wrap = normal +ij_java_extends_list_wrap = normal +ij_java_field_annotation_wrap = split_into_lines +ij_java_finally_on_new_line = false +ij_java_for_brace_force = always +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = false +ij_java_for_statement_wrap = on_every_item +ij_java_generate_final_locals = true +ij_java_generate_final_parameters = true +ij_java_if_brace_force = always +ij_java_imports_layout = *, |, javax.**, java.**, |, $* +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = false +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 2 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_builder_methods_indents = false +ij_java_keep_control_statement_in_one_line = true +ij_java_keep_first_column_comment = true +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = false +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = false +ij_java_keep_simple_lambdas_in_one_line = true +ij_java_keep_simple_methods_in_one_line = false +ij_java_label_indent_absolute = false +ij_java_label_indent_size = 0 +ij_java_lambda_brace_style = end_of_line +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = false +ij_java_line_comment_add_space_on_reformat = false +ij_java_line_comment_at_first_column = true +ij_java_message_dd_suffix = EJB +ij_java_message_eb_suffix = Bean +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = on_every_item +ij_java_method_parameters_new_line_after_left_paren = true +ij_java_method_parameters_right_paren_on_new_line = false +ij_java_method_parameters_wrap = on_every_item +ij_java_modifier_list_wrap = false +ij_java_multi_catch_types_wrap = normal +ij_java_names_count_to_use_import_on_demand = 50 +ij_java_new_line_after_lparen_in_annotation = false +ij_java_new_line_after_lparen_in_record_header = false +ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.* +ij_java_parameter_annotation_wrap = off +ij_java_parentheses_expression_new_line_after_left_paren = false +ij_java_parentheses_expression_right_paren_on_new_line = false +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_record_components_wrap = normal +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = false +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = false +ij_java_resource_list_right_paren_on_new_line = false +ij_java_resource_list_wrap = on_every_item +ij_java_rparen_on_new_line_in_annotation = false +ij_java_rparen_on_new_line_in_record_header = false +ij_java_session_dd_suffix = EJB +ij_java_session_eb_suffix = Bean +ij_java_session_hi_suffix = Home +ij_java_session_lhi_prefix = Local +ij_java_session_lhi_suffix = Home +ij_java_session_li_prefix = Local +ij_java_session_si_suffix = Service +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = false +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = false +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_annotation_eq = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = false +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_record_header = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_subclass_name_suffix = Impl +ij_java_ternary_operation_signs_on_next_line = false +ij_java_ternary_operation_wrap = on_every_item +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = normal +ij_java_throws_list_wrap = normal +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_relative_indents = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = off +ij_java_visibility = public +ij_java_while_brace_force = always +ij_java_while_on_new_line = false +ij_java_wrap_comments = true +ij_java_wrap_first_method_in_call_chain = false +ij_java_wrap_long_lines = false + +[*.less] +indent_size = 2 +ij_less_align_closing_brace_with_properties = false +ij_less_blank_lines_around_nested_selector = 1 +ij_less_blank_lines_between_blocks = 1 +ij_less_block_comment_add_space = false +ij_less_brace_placement = 0 +ij_less_enforce_quotes_on_format = false +ij_less_hex_color_long_format = false +ij_less_hex_color_lower_case = false +ij_less_hex_color_short_format = false +ij_less_hex_color_upper_case = false +ij_less_keep_blank_lines_in_code = 2 +ij_less_keep_indents_on_empty_lines = false +ij_less_keep_single_line_blocks = false +ij_less_line_comment_add_space = false +ij_less_line_comment_at_first_column = false +ij_less_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow +ij_less_space_after_colon = true +ij_less_space_before_opening_brace = true +ij_less_use_double_quotes = true +ij_less_value_alignment = 0 + +[*.proto] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_protobuf_keep_blank_lines_in_code = 2 +ij_protobuf_keep_indents_on_empty_lines = false +ij_protobuf_keep_line_breaks = true +ij_protobuf_space_after_comma = true +ij_protobuf_space_before_comma = false +ij_protobuf_spaces_around_assignment_operators = true +ij_protobuf_spaces_within_braces = false +ij_protobuf_spaces_within_brackets = false + +[*.sass] +indent_size = 2 +ij_sass_align_closing_brace_with_properties = false +ij_sass_blank_lines_around_nested_selector = 1 +ij_sass_blank_lines_between_blocks = 1 +ij_sass_brace_placement = 0 +ij_sass_enforce_quotes_on_format = false +ij_sass_hex_color_long_format = false +ij_sass_hex_color_lower_case = false +ij_sass_hex_color_short_format = false +ij_sass_hex_color_upper_case = false +ij_sass_keep_blank_lines_in_code = 2 +ij_sass_keep_indents_on_empty_lines = false +ij_sass_keep_single_line_blocks = false +ij_sass_line_comment_add_space = false +ij_sass_line_comment_at_first_column = false +ij_sass_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow +ij_sass_space_after_colon = true +ij_sass_space_before_opening_brace = true +ij_sass_use_double_quotes = true +ij_sass_value_alignment = 0 + +[*.scss] +indent_size = 2 +ij_scss_align_closing_brace_with_properties = false +ij_scss_blank_lines_around_nested_selector = 1 +ij_scss_blank_lines_between_blocks = 1 +ij_scss_block_comment_add_space = false +ij_scss_brace_placement = 0 +ij_scss_enforce_quotes_on_format = false +ij_scss_hex_color_long_format = false +ij_scss_hex_color_lower_case = false +ij_scss_hex_color_short_format = false +ij_scss_hex_color_upper_case = false +ij_scss_keep_blank_lines_in_code = 2 +ij_scss_keep_indents_on_empty_lines = false +ij_scss_keep_single_line_blocks = false +ij_scss_line_comment_add_space = false +ij_scss_line_comment_at_first_column = false +ij_scss_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow +ij_scss_space_after_colon = true +ij_scss_space_before_opening_brace = true +ij_scss_use_double_quotes = true +ij_scss_value_alignment = 0 + +[*.styl] +indent_size = 2 +ij_stylus_align_closing_brace_with_properties = false +ij_stylus_blank_lines_around_nested_selector = 1 +ij_stylus_blank_lines_between_blocks = 1 +ij_stylus_brace_placement = 0 +ij_stylus_enforce_quotes_on_format = false +ij_stylus_hex_color_long_format = false +ij_stylus_hex_color_lower_case = false +ij_stylus_hex_color_short_format = false +ij_stylus_hex_color_upper_case = false +ij_stylus_keep_blank_lines_in_code = 2 +ij_stylus_keep_indents_on_empty_lines = false +ij_stylus_keep_single_line_blocks = false +ij_stylus_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow +ij_stylus_space_after_colon = true +ij_stylus_space_before_opening_brace = true +ij_stylus_use_double_quotes = true +ij_stylus_value_alignment = 0 + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.ad,*.adoc,*.asciidoc,.asciidoctorconfig}] +ij_asciidoc_blank_lines_after_header = 1 +ij_asciidoc_blank_lines_keep_after_header = 1 +ij_asciidoc_formatting_enabled = true +ij_asciidoc_one_sentence_per_line = true + +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +ij_xml_block_comment_add_space = false +ij_xml_block_comment_at_first_column = true +ij_xml_keep_blank_lines = 2 +ij_xml_keep_indents_on_empty_lines = false +ij_xml_keep_line_breaks = true +ij_xml_keep_line_breaks_in_text = true +ij_xml_keep_whitespaces = false +ij_xml_keep_whitespaces_around_cdata = preserve +ij_xml_keep_whitespaces_inside_cdata = false +ij_xml_line_comment_at_first_column = true +ij_xml_space_after_tag_name = false +ij_xml_space_around_equals_in_attribute = false +ij_xml_space_inside_empty_tag = false +ij_xml_text_wrap = normal +ij_xml_use_custom_settings = false + +[{*.ats,*.cts,*.mts,*.ts}] +ij_continuation_indent_size = 4 +ij_typescript_align_imports = false +ij_typescript_align_multiline_array_initializer_expression = false +ij_typescript_align_multiline_binary_operation = false +ij_typescript_align_multiline_chained_methods = false +ij_typescript_align_multiline_extends_list = false +ij_typescript_align_multiline_for = true +ij_typescript_align_multiline_parameters = true +ij_typescript_align_multiline_parameters_in_calls = false +ij_typescript_align_multiline_ternary_operation = false +ij_typescript_align_object_properties = 0 +ij_typescript_align_union_types = false +ij_typescript_align_var_statements = 0 +ij_typescript_array_initializer_new_line_after_left_brace = false +ij_typescript_array_initializer_right_brace_on_new_line = false +ij_typescript_array_initializer_wrap = off +ij_typescript_assignment_wrap = off +ij_typescript_binary_operation_sign_on_next_line = false +ij_typescript_binary_operation_wrap = off +ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** +ij_typescript_blank_lines_after_imports = 1 +ij_typescript_blank_lines_around_class = 1 +ij_typescript_blank_lines_around_field = 0 +ij_typescript_blank_lines_around_field_in_interface = 0 +ij_typescript_blank_lines_around_function = 1 +ij_typescript_blank_lines_around_method = 1 +ij_typescript_blank_lines_around_method_in_interface = 1 +ij_typescript_block_brace_style = end_of_line +ij_typescript_block_comment_add_space = false +ij_typescript_block_comment_at_first_column = true +ij_typescript_call_parameters_new_line_after_left_paren = false +ij_typescript_call_parameters_right_paren_on_new_line = false +ij_typescript_call_parameters_wrap = off +ij_typescript_catch_on_new_line = false +ij_typescript_chained_call_dot_on_new_line = true +ij_typescript_class_brace_style = end_of_line +ij_typescript_comma_on_new_line = false +ij_typescript_do_while_brace_force = never +ij_typescript_else_on_new_line = false +ij_typescript_enforce_trailing_comma = keep +ij_typescript_enum_constants_wrap = on_every_item +ij_typescript_extends_keyword_wrap = off +ij_typescript_extends_list_wrap = off +ij_typescript_field_prefix = _ +ij_typescript_file_name_style = relaxed +ij_typescript_finally_on_new_line = false +ij_typescript_for_brace_force = never +ij_typescript_for_statement_new_line_after_left_paren = false +ij_typescript_for_statement_right_paren_on_new_line = false +ij_typescript_for_statement_wrap = off +ij_typescript_force_quote_style = false +ij_typescript_force_semicolon_style = false +ij_typescript_function_expression_brace_style = end_of_line +ij_typescript_if_brace_force = never +ij_typescript_import_merge_members = global +ij_typescript_import_prefer_absolute_path = global +ij_typescript_import_sort_members = true +ij_typescript_import_sort_module_name = false +ij_typescript_import_use_node_resolution = true +ij_typescript_imports_wrap = on_every_item +ij_typescript_indent_case_from_switch = true +ij_typescript_indent_chained_calls = true +ij_typescript_indent_package_children = 0 +ij_typescript_jsdoc_include_types = false +ij_typescript_jsx_attribute_value = braces +ij_typescript_keep_blank_lines_in_code = 2 +ij_typescript_keep_first_column_comment = true +ij_typescript_keep_indents_on_empty_lines = false +ij_typescript_keep_line_breaks = true +ij_typescript_keep_simple_blocks_in_one_line = false +ij_typescript_keep_simple_methods_in_one_line = false +ij_typescript_line_comment_add_space = true +ij_typescript_line_comment_at_first_column = false +ij_typescript_method_brace_style = end_of_line +ij_typescript_method_call_chain_wrap = off +ij_typescript_method_parameters_new_line_after_left_paren = false +ij_typescript_method_parameters_right_paren_on_new_line = false +ij_typescript_method_parameters_wrap = off +ij_typescript_object_literal_wrap = on_every_item +ij_typescript_parentheses_expression_new_line_after_left_paren = false +ij_typescript_parentheses_expression_right_paren_on_new_line = false +ij_typescript_place_assignment_sign_on_next_line = false +ij_typescript_prefer_as_type_cast = false +ij_typescript_prefer_explicit_types_function_expression_returns = false +ij_typescript_prefer_explicit_types_function_returns = false +ij_typescript_prefer_explicit_types_vars_fields = false +ij_typescript_prefer_parameters_wrap = false +ij_typescript_reformat_c_style_comments = false +ij_typescript_space_after_colon = true +ij_typescript_space_after_comma = true +ij_typescript_space_after_dots_in_rest_parameter = false +ij_typescript_space_after_generator_mult = true +ij_typescript_space_after_property_colon = true +ij_typescript_space_after_quest = true +ij_typescript_space_after_type_colon = true +ij_typescript_space_after_unary_not = false +ij_typescript_space_before_async_arrow_lparen = true +ij_typescript_space_before_catch_keyword = true +ij_typescript_space_before_catch_left_brace = true +ij_typescript_space_before_catch_parentheses = true +ij_typescript_space_before_class_lbrace = true +ij_typescript_space_before_class_left_brace = true +ij_typescript_space_before_colon = true +ij_typescript_space_before_comma = false +ij_typescript_space_before_do_left_brace = true +ij_typescript_space_before_else_keyword = true +ij_typescript_space_before_else_left_brace = true +ij_typescript_space_before_finally_keyword = true +ij_typescript_space_before_finally_left_brace = true +ij_typescript_space_before_for_left_brace = true +ij_typescript_space_before_for_parentheses = true +ij_typescript_space_before_for_semicolon = false +ij_typescript_space_before_function_left_parenth = true +ij_typescript_space_before_generator_mult = false +ij_typescript_space_before_if_left_brace = true +ij_typescript_space_before_if_parentheses = true +ij_typescript_space_before_method_call_parentheses = false +ij_typescript_space_before_method_left_brace = true +ij_typescript_space_before_method_parentheses = false +ij_typescript_space_before_property_colon = false +ij_typescript_space_before_quest = true +ij_typescript_space_before_switch_left_brace = true +ij_typescript_space_before_switch_parentheses = true +ij_typescript_space_before_try_left_brace = true +ij_typescript_space_before_type_colon = false +ij_typescript_space_before_unary_not = false +ij_typescript_space_before_while_keyword = true +ij_typescript_space_before_while_left_brace = true +ij_typescript_space_before_while_parentheses = true +ij_typescript_spaces_around_additive_operators = true +ij_typescript_spaces_around_arrow_function_operator = true +ij_typescript_spaces_around_assignment_operators = true +ij_typescript_spaces_around_bitwise_operators = true +ij_typescript_spaces_around_equality_operators = true +ij_typescript_spaces_around_logical_operators = true +ij_typescript_spaces_around_multiplicative_operators = true +ij_typescript_spaces_around_relational_operators = true +ij_typescript_spaces_around_shift_operators = true +ij_typescript_spaces_around_unary_operator = false +ij_typescript_spaces_within_array_initializer_brackets = false +ij_typescript_spaces_within_brackets = false +ij_typescript_spaces_within_catch_parentheses = false +ij_typescript_spaces_within_for_parentheses = false +ij_typescript_spaces_within_if_parentheses = false +ij_typescript_spaces_within_imports = false +ij_typescript_spaces_within_interpolation_expressions = false +ij_typescript_spaces_within_method_call_parentheses = false +ij_typescript_spaces_within_method_parentheses = false +ij_typescript_spaces_within_object_literal_braces = false +ij_typescript_spaces_within_object_type_braces = true +ij_typescript_spaces_within_parentheses = false +ij_typescript_spaces_within_switch_parentheses = false +ij_typescript_spaces_within_type_assertion = false +ij_typescript_spaces_within_union_types = true +ij_typescript_spaces_within_while_parentheses = false +ij_typescript_special_else_if_treatment = true +ij_typescript_ternary_operation_signs_on_next_line = false +ij_typescript_ternary_operation_wrap = off +ij_typescript_union_types_wrap = on_every_item +ij_typescript_use_chained_calls_group_indents = false +ij_typescript_use_double_quotes = true +ij_typescript_use_explicit_js_extension = auto +ij_typescript_use_path_mapping = always +ij_typescript_use_public_modifier = false +ij_typescript_use_semicolon_after_statement = true +ij_typescript_var_declaration_wrap = normal +ij_typescript_while_brace_force = never +ij_typescript_while_on_new_line = false +ij_typescript_wrap_comments = false + +[{*.bash,*.sh,*.zsh}] +indent_size = 2 +tab_width = 2 +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false +ij_shell_use_unix_line_separator = true + +[{*.cjs,*.js}] +ij_continuation_indent_size = 4 +ij_javascript_align_imports = false +ij_javascript_align_multiline_array_initializer_expression = false +ij_javascript_align_multiline_binary_operation = false +ij_javascript_align_multiline_chained_methods = false +ij_javascript_align_multiline_extends_list = false +ij_javascript_align_multiline_for = true +ij_javascript_align_multiline_parameters = true +ij_javascript_align_multiline_parameters_in_calls = false +ij_javascript_align_multiline_ternary_operation = false +ij_javascript_align_object_properties = 0 +ij_javascript_align_union_types = false +ij_javascript_align_var_statements = 0 +ij_javascript_array_initializer_new_line_after_left_brace = false +ij_javascript_array_initializer_right_brace_on_new_line = false +ij_javascript_array_initializer_wrap = off +ij_javascript_assignment_wrap = off +ij_javascript_binary_operation_sign_on_next_line = false +ij_javascript_binary_operation_wrap = off +ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** +ij_javascript_blank_lines_after_imports = 1 +ij_javascript_blank_lines_around_class = 1 +ij_javascript_blank_lines_around_field = 0 +ij_javascript_blank_lines_around_function = 1 +ij_javascript_blank_lines_around_method = 1 +ij_javascript_block_brace_style = end_of_line +ij_javascript_block_comment_add_space = false +ij_javascript_block_comment_at_first_column = true +ij_javascript_call_parameters_new_line_after_left_paren = false +ij_javascript_call_parameters_right_paren_on_new_line = false +ij_javascript_call_parameters_wrap = off +ij_javascript_catch_on_new_line = false +ij_javascript_chained_call_dot_on_new_line = true +ij_javascript_class_brace_style = end_of_line +ij_javascript_comma_on_new_line = false +ij_javascript_do_while_brace_force = never +ij_javascript_else_on_new_line = false +ij_javascript_enforce_trailing_comma = keep +ij_javascript_extends_keyword_wrap = off +ij_javascript_extends_list_wrap = off +ij_javascript_field_prefix = _ +ij_javascript_file_name_style = relaxed +ij_javascript_finally_on_new_line = false +ij_javascript_for_brace_force = never +ij_javascript_for_statement_new_line_after_left_paren = false +ij_javascript_for_statement_right_paren_on_new_line = false +ij_javascript_for_statement_wrap = off +ij_javascript_force_quote_style = false +ij_javascript_force_semicolon_style = false +ij_javascript_function_expression_brace_style = end_of_line +ij_javascript_if_brace_force = never +ij_javascript_import_merge_members = global +ij_javascript_import_prefer_absolute_path = global +ij_javascript_import_sort_members = true +ij_javascript_import_sort_module_name = false +ij_javascript_import_use_node_resolution = true +ij_javascript_imports_wrap = on_every_item +ij_javascript_indent_case_from_switch = true +ij_javascript_indent_chained_calls = true +ij_javascript_indent_package_children = 0 +ij_javascript_jsx_attribute_value = braces +ij_javascript_keep_blank_lines_in_code = 2 +ij_javascript_keep_first_column_comment = true +ij_javascript_keep_indents_on_empty_lines = false +ij_javascript_keep_line_breaks = true +ij_javascript_keep_simple_blocks_in_one_line = false +ij_javascript_keep_simple_methods_in_one_line = false +ij_javascript_line_comment_add_space = true +ij_javascript_line_comment_at_first_column = false +ij_javascript_method_brace_style = end_of_line +ij_javascript_method_call_chain_wrap = off +ij_javascript_method_parameters_new_line_after_left_paren = false +ij_javascript_method_parameters_right_paren_on_new_line = false +ij_javascript_method_parameters_wrap = off +ij_javascript_object_literal_wrap = on_every_item +ij_javascript_parentheses_expression_new_line_after_left_paren = false +ij_javascript_parentheses_expression_right_paren_on_new_line = false +ij_javascript_place_assignment_sign_on_next_line = false +ij_javascript_prefer_as_type_cast = false +ij_javascript_prefer_explicit_types_function_expression_returns = false +ij_javascript_prefer_explicit_types_function_returns = false +ij_javascript_prefer_explicit_types_vars_fields = false +ij_javascript_prefer_parameters_wrap = false +ij_javascript_reformat_c_style_comments = false +ij_javascript_space_after_colon = true +ij_javascript_space_after_comma = true +ij_javascript_space_after_dots_in_rest_parameter = false +ij_javascript_space_after_generator_mult = true +ij_javascript_space_after_property_colon = true +ij_javascript_space_after_quest = true +ij_javascript_space_after_type_colon = true +ij_javascript_space_after_unary_not = false +ij_javascript_space_before_async_arrow_lparen = true +ij_javascript_space_before_catch_keyword = true +ij_javascript_space_before_catch_left_brace = true +ij_javascript_space_before_catch_parentheses = true +ij_javascript_space_before_class_lbrace = true +ij_javascript_space_before_class_left_brace = true +ij_javascript_space_before_colon = true +ij_javascript_space_before_comma = false +ij_javascript_space_before_do_left_brace = true +ij_javascript_space_before_else_keyword = true +ij_javascript_space_before_else_left_brace = true +ij_javascript_space_before_finally_keyword = true +ij_javascript_space_before_finally_left_brace = true +ij_javascript_space_before_for_left_brace = true +ij_javascript_space_before_for_parentheses = true +ij_javascript_space_before_for_semicolon = false +ij_javascript_space_before_function_left_parenth = true +ij_javascript_space_before_generator_mult = false +ij_javascript_space_before_if_left_brace = true +ij_javascript_space_before_if_parentheses = true +ij_javascript_space_before_method_call_parentheses = false +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = false +ij_javascript_space_before_property_colon = false +ij_javascript_space_before_quest = true +ij_javascript_space_before_switch_left_brace = true +ij_javascript_space_before_switch_parentheses = true +ij_javascript_space_before_try_left_brace = true +ij_javascript_space_before_type_colon = false +ij_javascript_space_before_unary_not = false +ij_javascript_space_before_while_keyword = true +ij_javascript_space_before_while_left_brace = true +ij_javascript_space_before_while_parentheses = true +ij_javascript_spaces_around_additive_operators = true +ij_javascript_spaces_around_arrow_function_operator = true +ij_javascript_spaces_around_assignment_operators = true +ij_javascript_spaces_around_bitwise_operators = true +ij_javascript_spaces_around_equality_operators = true +ij_javascript_spaces_around_logical_operators = true +ij_javascript_spaces_around_multiplicative_operators = true +ij_javascript_spaces_around_relational_operators = true +ij_javascript_spaces_around_shift_operators = true +ij_javascript_spaces_around_unary_operator = false +ij_javascript_spaces_within_array_initializer_brackets = false +ij_javascript_spaces_within_brackets = false +ij_javascript_spaces_within_catch_parentheses = false +ij_javascript_spaces_within_for_parentheses = false +ij_javascript_spaces_within_if_parentheses = false +ij_javascript_spaces_within_imports = false +ij_javascript_spaces_within_interpolation_expressions = false +ij_javascript_spaces_within_method_call_parentheses = false +ij_javascript_spaces_within_method_parentheses = false +ij_javascript_spaces_within_object_literal_braces = false +ij_javascript_spaces_within_object_type_braces = true +ij_javascript_spaces_within_parentheses = false +ij_javascript_spaces_within_switch_parentheses = false +ij_javascript_spaces_within_type_assertion = false +ij_javascript_spaces_within_union_types = true +ij_javascript_spaces_within_while_parentheses = false +ij_javascript_special_else_if_treatment = true +ij_javascript_ternary_operation_signs_on_next_line = false +ij_javascript_ternary_operation_wrap = off +ij_javascript_union_types_wrap = on_every_item +ij_javascript_use_chained_calls_group_indents = false +ij_javascript_use_double_quotes = true +ij_javascript_use_explicit_js_extension = auto +ij_javascript_use_path_mapping = always +ij_javascript_use_public_modifier = false +ij_javascript_use_semicolon_after_statement = true +ij_javascript_var_declaration_wrap = normal +ij_javascript_while_brace_force = never +ij_javascript_while_on_new_line = false +ij_javascript_wrap_comments = false + +[{*.ft,*.vm,*.vsl}] +ij_vtl_keep_indents_on_empty_lines = false + +[{*.gant,*.groovy,*.gy}] +ij_groovy_align_group_field_declarations = false +ij_groovy_align_multiline_array_initializer_expression = false +ij_groovy_align_multiline_assignment = false +ij_groovy_align_multiline_binary_operation = false +ij_groovy_align_multiline_chained_methods = false +ij_groovy_align_multiline_extends_list = false +ij_groovy_align_multiline_for = true +ij_groovy_align_multiline_list_or_map = true +ij_groovy_align_multiline_method_parentheses = false +ij_groovy_align_multiline_parameters = true +ij_groovy_align_multiline_parameters_in_calls = false +ij_groovy_align_multiline_resources = true +ij_groovy_align_multiline_ternary_operation = false +ij_groovy_align_multiline_throws_list = false +ij_groovy_align_named_args_in_map = true +ij_groovy_align_throws_keyword = false +ij_groovy_array_initializer_new_line_after_left_brace = false +ij_groovy_array_initializer_right_brace_on_new_line = false +ij_groovy_array_initializer_wrap = off +ij_groovy_assert_statement_wrap = off +ij_groovy_assignment_wrap = off +ij_groovy_binary_operation_wrap = off +ij_groovy_blank_lines_after_class_header = 0 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_block_brace_style = end_of_line +ij_groovy_block_comment_add_space = false +ij_groovy_block_comment_at_first_column = true +ij_groovy_call_parameters_new_line_after_left_paren = false +ij_groovy_call_parameters_right_paren_on_new_line = false +ij_groovy_call_parameters_wrap = off +ij_groovy_catch_on_new_line = false +ij_groovy_class_annotation_wrap = split_into_lines +ij_groovy_class_brace_style = end_of_line +ij_groovy_class_count_to_use_import_on_demand = 5 +ij_groovy_do_while_brace_force = never +ij_groovy_else_on_new_line = false +ij_groovy_enable_groovydoc_formatting = true +ij_groovy_enum_constants_wrap = off +ij_groovy_extends_keyword_wrap = off +ij_groovy_extends_list_wrap = off +ij_groovy_field_annotation_wrap = split_into_lines +ij_groovy_finally_on_new_line = false +ij_groovy_for_brace_force = never +ij_groovy_for_statement_new_line_after_left_paren = false +ij_groovy_for_statement_right_paren_on_new_line = false +ij_groovy_for_statement_wrap = off +ij_groovy_if_brace_force = never +ij_groovy_import_annotation_wrap = 2 +ij_groovy_imports_layout = *, |, javax.**, java.**, |, $* +ij_groovy_indent_case_from_switch = true +ij_groovy_indent_label_blocks = true +ij_groovy_insert_inner_class_imports = false +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 2 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = true +ij_groovy_keep_first_column_comment = true +ij_groovy_keep_indents_on_empty_lines = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_label_indent_absolute = false +ij_groovy_label_indent_size = 0 +ij_groovy_lambda_brace_style = end_of_line +ij_groovy_layout_static_imports_separately = true +ij_groovy_line_comment_add_space = false +ij_groovy_line_comment_add_space_on_reformat = false +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_annotation_wrap = split_into_lines +ij_groovy_method_brace_style = end_of_line +ij_groovy_method_call_chain_wrap = off +ij_groovy_method_parameters_new_line_after_left_paren = false +ij_groovy_method_parameters_right_paren_on_new_line = false +ij_groovy_method_parameters_wrap = off +ij_groovy_modifier_list_wrap = false +ij_groovy_names_count_to_use_import_on_demand = 3 +ij_groovy_packages_to_use_import_on_demand = java.awt.*, javax.swing.* +ij_groovy_parameter_annotation_wrap = off +ij_groovy_parentheses_expression_new_line_after_left_paren = false +ij_groovy_parentheses_expression_right_paren_on_new_line = false +ij_groovy_prefer_parameters_wrap = false +ij_groovy_resource_list_new_line_after_left_paren = false +ij_groovy_resource_list_right_paren_on_new_line = false +ij_groovy_resource_list_wrap = off +ij_groovy_space_after_assert_separator = true +ij_groovy_space_after_colon = true +ij_groovy_space_after_comma = true +ij_groovy_space_after_comma_in_type_arguments = true +ij_groovy_space_after_for_semicolon = true +ij_groovy_space_after_quest = true +ij_groovy_space_after_type_cast = true +ij_groovy_space_before_annotation_parameter_list = false +ij_groovy_space_before_array_initializer_left_brace = false +ij_groovy_space_before_assert_separator = false +ij_groovy_space_before_catch_keyword = true +ij_groovy_space_before_catch_left_brace = true +ij_groovy_space_before_catch_parentheses = true +ij_groovy_space_before_class_left_brace = true +ij_groovy_space_before_closure_left_brace = true +ij_groovy_space_before_colon = true +ij_groovy_space_before_comma = false +ij_groovy_space_before_do_left_brace = true +ij_groovy_space_before_else_keyword = true +ij_groovy_space_before_else_left_brace = true +ij_groovy_space_before_finally_keyword = true +ij_groovy_space_before_finally_left_brace = true +ij_groovy_space_before_for_left_brace = true +ij_groovy_space_before_for_parentheses = true +ij_groovy_space_before_for_semicolon = false +ij_groovy_space_before_if_left_brace = true +ij_groovy_space_before_if_parentheses = true +ij_groovy_space_before_method_call_parentheses = false +ij_groovy_space_before_method_left_brace = true +ij_groovy_space_before_method_parentheses = false +ij_groovy_space_before_quest = true +ij_groovy_space_before_record_parentheses = false +ij_groovy_space_before_switch_left_brace = true +ij_groovy_space_before_switch_parentheses = true +ij_groovy_space_before_synchronized_left_brace = true +ij_groovy_space_before_synchronized_parentheses = true +ij_groovy_space_before_try_left_brace = true +ij_groovy_space_before_try_parentheses = true +ij_groovy_space_before_while_keyword = true +ij_groovy_space_before_while_left_brace = true +ij_groovy_space_before_while_parentheses = true +ij_groovy_space_in_named_argument = true +ij_groovy_space_in_named_argument_before_colon = false +ij_groovy_space_within_empty_array_initializer_braces = false +ij_groovy_space_within_empty_method_call_parentheses = false +ij_groovy_spaces_around_additive_operators = true +ij_groovy_spaces_around_assignment_operators = true +ij_groovy_spaces_around_bitwise_operators = true +ij_groovy_spaces_around_equality_operators = true +ij_groovy_spaces_around_lambda_arrow = true +ij_groovy_spaces_around_logical_operators = true +ij_groovy_spaces_around_multiplicative_operators = true +ij_groovy_spaces_around_regex_operators = true +ij_groovy_spaces_around_relational_operators = true +ij_groovy_spaces_around_shift_operators = true +ij_groovy_spaces_within_annotation_parentheses = false +ij_groovy_spaces_within_array_initializer_braces = false +ij_groovy_spaces_within_braces = true +ij_groovy_spaces_within_brackets = false +ij_groovy_spaces_within_cast_parentheses = false +ij_groovy_spaces_within_catch_parentheses = false +ij_groovy_spaces_within_for_parentheses = false +ij_groovy_spaces_within_gstring_injection_braces = false +ij_groovy_spaces_within_if_parentheses = false +ij_groovy_spaces_within_list_or_map = false +ij_groovy_spaces_within_method_call_parentheses = false +ij_groovy_spaces_within_method_parentheses = false +ij_groovy_spaces_within_parentheses = false +ij_groovy_spaces_within_switch_parentheses = false +ij_groovy_spaces_within_synchronized_parentheses = false +ij_groovy_spaces_within_try_parentheses = false +ij_groovy_spaces_within_tuple_expression = false +ij_groovy_spaces_within_while_parentheses = false +ij_groovy_special_else_if_treatment = true +ij_groovy_ternary_operation_wrap = off +ij_groovy_throws_keyword_wrap = off +ij_groovy_throws_list_wrap = off +ij_groovy_use_flying_geese_braces = false +ij_groovy_use_fq_class_names = false +ij_groovy_use_fq_class_names_in_javadoc = true +ij_groovy_use_relative_indents = false +ij_groovy_use_single_class_imports = true +ij_groovy_variable_annotation_wrap = off +ij_groovy_while_brace_force = never +ij_groovy_while_on_new_line = false +ij_groovy_wrap_chain_calls_after_dot = false +ij_groovy_wrap_long_lines = false + +[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}] +indent_size = 2 +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = true +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] +ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_add_space = false +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p +ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span, pre, textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}] +ij_jsp_jsp_prefer_comma_separated_import_list = false +ij_jsp_keep_indents_on_empty_lines = false + +[{*.jspx,*.tagx}] +ij_jspx_keep_indents_on_empty_lines = false + +[{*.kt,*.kts}] +ij_kotlin_align_in_columns_case_branch = false +ij_kotlin_align_multiline_binary_operation = false +ij_kotlin_align_multiline_extends_list = false +ij_kotlin_align_multiline_method_parentheses = false +ij_kotlin_align_multiline_parameters = true +ij_kotlin_align_multiline_parameters_in_calls = false +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_assignment_wrap = normal +ij_kotlin_blank_lines_after_class_header = 0 +ij_kotlin_blank_lines_around_block_when_branches = 0 +ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 +ij_kotlin_block_comment_add_space = false +ij_kotlin_block_comment_at_first_column = true +ij_kotlin_call_parameters_new_line_after_left_paren = true +ij_kotlin_call_parameters_right_paren_on_new_line = true +ij_kotlin_call_parameters_wrap = on_every_item +ij_kotlin_catch_on_new_line = false +ij_kotlin_class_annotation_wrap = split_into_lines +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL +ij_kotlin_continuation_indent_for_chained_calls = false +ij_kotlin_continuation_indent_for_expression_bodies = false +ij_kotlin_continuation_indent_in_argument_lists = false +ij_kotlin_continuation_indent_in_elvis = false +ij_kotlin_continuation_indent_in_if_conditions = false +ij_kotlin_continuation_indent_in_parameter_lists = false +ij_kotlin_continuation_indent_in_supertype_lists = false +ij_kotlin_else_on_new_line = false +ij_kotlin_enum_constants_wrap = off +ij_kotlin_extends_list_wrap = normal +ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_finally_on_new_line = false +ij_kotlin_if_rparen_on_new_line = true +ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ +ij_kotlin_insert_whitespaces_in_simple_one_line_method = true +ij_kotlin_keep_blank_lines_before_right_brace = 2 +ij_kotlin_keep_blank_lines_in_code = 2 +ij_kotlin_keep_blank_lines_in_declarations = 2 +ij_kotlin_keep_first_column_comment = true +ij_kotlin_keep_indents_on_empty_lines = false +ij_kotlin_keep_line_breaks = true +ij_kotlin_lbrace_on_next_line = false +ij_kotlin_line_comment_add_space = false +ij_kotlin_line_comment_add_space_on_reformat = false +ij_kotlin_line_comment_at_first_column = true +ij_kotlin_method_annotation_wrap = split_into_lines +ij_kotlin_method_call_chain_wrap = normal +ij_kotlin_method_parameters_new_line_after_left_paren = true +ij_kotlin_method_parameters_right_paren_on_new_line = true +ij_kotlin_method_parameters_wrap = on_every_item +ij_kotlin_name_count_to_use_star_import = 5 +ij_kotlin_name_count_to_use_star_import_for_members = 3 +ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.** +ij_kotlin_parameter_annotation_wrap = off +ij_kotlin_space_after_comma = true +ij_kotlin_space_after_extend_colon = true +ij_kotlin_space_after_type_colon = true +ij_kotlin_space_before_catch_parentheses = true +ij_kotlin_space_before_comma = false +ij_kotlin_space_before_extend_colon = true +ij_kotlin_space_before_for_parentheses = true +ij_kotlin_space_before_if_parentheses = true +ij_kotlin_space_before_lambda_arrow = true +ij_kotlin_space_before_type_colon = false +ij_kotlin_space_before_when_parentheses = true +ij_kotlin_space_before_while_parentheses = true +ij_kotlin_spaces_around_additive_operators = true +ij_kotlin_spaces_around_assignment_operators = true +ij_kotlin_spaces_around_equality_operators = true +ij_kotlin_spaces_around_function_type_arrow = true +ij_kotlin_spaces_around_logical_operators = true +ij_kotlin_spaces_around_multiplicative_operators = true +ij_kotlin_spaces_around_range = false +ij_kotlin_spaces_around_relational_operators = true +ij_kotlin_spaces_around_unary_operator = false +ij_kotlin_spaces_around_when_arrow = true +ij_kotlin_variable_annotation_wrap = off +ij_kotlin_while_on_new_line = false +ij_kotlin_wrap_elvis_expressions = 1 +ij_kotlin_wrap_expression_body_functions = 1 +ij_kotlin_wrap_first_method_in_call_chain = false + +[{*.markdown,*.md}] +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true +ij_markdown_insert_quote_arrows_on_wrap = true +ij_markdown_keep_indents_on_empty_lines = false +ij_markdown_keep_line_breaks_inside_text_blocks = true +ij_markdown_max_lines_around_block_elements = 1 +ij_markdown_max_lines_around_header = 1 +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_min_lines_around_header = 1 +ij_markdown_min_lines_between_paragraphs = 1 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true + +[{*.pb,*.textproto}] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_prototext_keep_blank_lines_in_code = 2 +ij_prototext_keep_indents_on_empty_lines = false +ij_prototext_keep_line_breaks = true +ij_prototext_space_after_colon = true +ij_prototext_space_after_comma = true +ij_prototext_space_before_colon = false +ij_prototext_space_before_comma = false +ij_prototext_spaces_within_braces = true +ij_prototext_spaces_within_brackets = false + +[{*.properties,spring.handlers,spring.schemas}] +ij_properties_align_group_field_declarations = false +ij_properties_keep_blank_lines = false +ij_properties_key_value_delimiter = equals +ij_properties_spaces_around_key_value_delimiter = false + +[{*.qute.html,*.qute.json,*.qute.txt,*.qute.yaml,*.qute.yml}] +ij_qute_keep_indents_on_empty_lines = false + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] +ij_toml_keep_indents_on_empty_lines = false + +[{*.yaml,*.yml}] +indent_size = 2 +ij_yaml_align_values_properties = do_not_align +ij_yaml_autoinsert_sequence_marker = true +ij_yaml_block_mapping_on_new_line = false +ij_yaml_indent_sequence_value = true +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true +ij_yaml_sequence_on_new_line = false +ij_yaml_space_before_colon = false +ij_yaml_spaces_within_braces = true +ij_yaml_spaces_within_brackets = true diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index dd081d9d1..e36465a8e 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,53 +1,335 @@ - - \ No newline at end of file + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index adef587b5..d77899371 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index 3b374d7c9..431396a86 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -35,7 +35,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class PublishST { +@SuppressWarnings("NewClassNamingConvention") +class PublishST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @@ -55,14 +56,16 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_publish() throws Exception { - final List publishCommand = List.of( - "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-t", "test", - "-m", "test", - "-d" - ); + final List publishCommand = List.of("pub", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-t", + "test", + "-m", + "test", + "-d"); final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() .identifier("subscriber") @@ -71,7 +74,11 @@ void test_successful_publish() throws Exception { .buildBlocking(); subscriber.connect(); final CountDownLatch receivedPublish = new CountDownLatch(1); - subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> receivedPublish.countDown()).send(); + subscriber.toAsync() + .subscribeWith() + .topicFilter("test") + .callback(ignored -> receivedPublish.countDown()) + .send(); final ExecutionResult executionResult = mqttCli.execute(publishCommand); @@ -86,11 +93,8 @@ void test_successful_publish() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_topic() throws Exception { - final List publishCommand = List.of( - "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()) - ); + final List publishCommand = + List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort())); final ExecutionResult executionResult = mqttCli.execute(publishCommand); @@ -101,17 +105,17 @@ void test_publish_missing_topic() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_message() throws Exception { - final List publishCommand = List.of( - "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-t", "test" - ); + final List publishCommand = + List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test"); final ExecutionResult executionResult = mqttCli.execute(publishCommand); assertEquals(2, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m= | -m:file=)") - || executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m:file= | -m=)")); + assertTrue(executionResult.getErrorOutput() + .contains( + "Error: Missing required argument (specify one of these): (-m= | -m:file=)") || + executionResult.getErrorOutput() + .contains( + "Error: Missing required argument (specify one of these): (-m:file= | -m=)")); } -} \ No newline at end of file +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 86319b989..43f532d4f 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -36,7 +36,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class SubscribeST { +@SuppressWarnings("NewClassNamingConvention") +class SubscribeST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq-ce")); @@ -56,13 +57,8 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_subscribe() throws Exception { - final List subscribeCommand = List.of( - "sub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-t", "test", - "-d" - ); + final List subscribeCommand = + List.of("sub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test", "-d"); final Mqtt5BlockingClient publisher = Mqtt5Client.builder() .identifier("publisher") @@ -83,11 +79,8 @@ void test_successful_subscribe() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_subscribe_missing_topic() throws Exception { - final List subscribeCommand = List.of( - "sub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()) - ); + final List subscribeCommand = + List.of("sub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort())); final ExecutionResult executionResult = mqttCli.execute(subscribeCommand); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java index ccd040967..22e51fcc9 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java @@ -29,13 +29,14 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class ConnectST { +@SuppressWarnings("NewClassNamingConvention") +class ConnectST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - final MqttCliShell mqttCliShell = new MqttCliShell(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -50,30 +51,17 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_connect() throws Exception { - final List connectCommand = List.of( - "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-i", "cliTest" - ); + final List connectCommand = + List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-i", "cliTest"); mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } - @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_unsuccessful_connect() throws Exception { - final List connectCommand = List.of( - "con", - "-h", "localhost", - "-p", "22", - "-i", "cliTest" - ); + final List connectCommand = List.of("con", "-h", "localhost", "-p", "22", "-i", "cliTest"); - mqttCliShell.executeAsync(connectCommand) - .awaitStdErr("Unable to connect.") - .awaitStdOut("mqtt>"); + mqttCliShell.executeAsync(connectCommand).awaitStdErr("Unable to connect.").awaitStdOut("mqtt>"); } - } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java index d10a3b24e..c24c5a396 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java @@ -29,7 +29,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class DisconnectST { +@SuppressWarnings("NewClassNamingConvention") +class DisconnectST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @@ -52,7 +53,7 @@ static void afterAll() { void test_successful_disconnect() throws Exception { final List disconnectCommand = List.of("dis"); mqttCliShell.connectClient(hivemq); - mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>"); + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>"); } @Test diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java index 3fb742830..a9ddcf544 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java @@ -29,7 +29,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class PublishST { +@SuppressWarnings("NewClassNamingConvention") +class PublishST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java index 314995e01..8e003b7e3 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java @@ -29,7 +29,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class SubscribeST { +@SuppressWarnings("NewClassNamingConvention") +class SubscribeST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @@ -57,7 +58,7 @@ void test_successful_subscribe() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_subscribe_missing_topic() throws Exception{ + void test_subscribe_missing_topic() throws Exception { final List subscribeCommand = List.of("sub"); mqttCliShell.connectClient(hivemq); mqttCliShell.executeAsync(subscribeCommand) diff --git a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java index deb79b382..fe50f24e9 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java +++ b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java @@ -32,7 +32,9 @@ public AwaitOutput(final @NotNull ProcessIO processIO, final @NotNull String com try { processIO.awaitStdOut(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Command '%s' did not return expected standard output '%s' in time.", command, expectedOutput), timeoutException); + Assertions.fail(String.format("Command '%s' did not return expected standard output '%s' in time.", + command, + expectedOutput), timeoutException); } return this; } @@ -41,7 +43,9 @@ public AwaitOutput(final @NotNull ProcessIO processIO, final @NotNull String com try { processIO.awaitStdErr(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Command '%s' did not return expected error output '%s' in time.", command, expectedOutput), timeoutException); + Assertions.fail(String.format("Command '%s' did not return expected error output '%s' in time.", + command, + expectedOutput), timeoutException); } return this; } diff --git a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java index bdb4f27a2..8a02c9b91 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java @@ -22,7 +22,8 @@ public class ExecutionResult { private final @NotNull String standardOutput; private final @NotNull String errorOutput; - public ExecutionResult(final int exitCode, final @NotNull String standardOutput, final @NotNull String errorOutput) { + public ExecutionResult( + final int exitCode, final @NotNull String standardOutput, final @NotNull String errorOutput) { this.exitCode = exitCode; this.standardOutput = standardOutput; this.errorOutput = errorOutput; diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java index f67d1a4e8..10362596d 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java @@ -35,7 +35,8 @@ public class MqttCli { Arrays.stream(Objects.requireNonNull(System.getProperty("cliExec")).split(" ")) .collect(Collectors.toUnmodifiableList()); - public @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { + public @NotNull ExecutionResult execute(final @NotNull List command) + throws IOException, InterruptedException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); @@ -49,8 +50,7 @@ public class MqttCli { return new ExecutionResult(exitCode, stdOut, stdErr); } - public @NotNull AwaitOutput executeAsync(final @NotNull List command) - throws IOException { + public @NotNull AwaitOutput executeAsync(final @NotNull List command) throws IOException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); @@ -59,5 +59,4 @@ public class MqttCli { return new AwaitOutput(processIO, String.join(" ", command)); } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index eed72b5e0..a8ae72fea 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -17,6 +17,7 @@ import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -24,14 +25,15 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; import static org.junit.jupiter.api.Assertions.assertTrue; public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { - private ProcessIO processIO; - private Process process; + private @Nullable ProcessIO processIO; + private @Nullable Process process; @Override public void beforeEach(final @NotNull ExtensionContext context) throws Exception { @@ -40,14 +42,14 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception @Override public void afterEach(final @NotNull ExtensionContext context) { - if (process.isAlive()) { + if (process != null && process.isAlive()) { process.destroy(); } } private void startShellMode() throws IOException { final List shellCommand = new ArrayList<>(CLI_EXEC); - assertTrue(shellCommand.addAll(List.of("sh"))); + assertTrue(shellCommand.add("sh")); this.process = new ProcessBuilder(shellCommand).start(); this.processIO = ProcessIO.startReading(process); @@ -65,9 +67,7 @@ public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) th public @NotNull AwaitOutput executeAsync(final @NotNull List command) throws IOException { final String fullCommand = String.join(" ", command); - processIO.writeMsg(fullCommand); + Objects.requireNonNull(processIO).writeMsg(fullCommand); return new AwaitOutput(processIO, fullCommand); } - - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java index 364542d8f..21faad863 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils; import org.jetbrains.annotations.NotNull; @@ -28,7 +29,7 @@ import static org.testcontainers.shaded.org.awaitility.Awaitility.await; -public class ProcessIO { +class ProcessIO { private final @NotNull BufferedWriter stdoutWriter; private final @NotNull StringBuilder processStdOut; @@ -166,5 +167,4 @@ public void writeMsg(final @NotNull String message) throws IOException { processOutputWriter.write('\n'); processOutputWriter.flush(); } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java b/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java index af8f8fd49..5313a6135 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java +++ b/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java @@ -17,9 +17,9 @@ import org.jetbrains.annotations.NotNull; -public class TimeoutException extends Exception { +class TimeoutException extends Exception { - public TimeoutException(final @NotNull String message, final @NotNull Throwable cause) { + TimeoutException(final @NotNull String message, final @NotNull Throwable cause) { super(message, cause); } } From ba89e5b6d40153882027f9273003206d9bbc2231 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Fri, 14 Oct 2022 15:01:45 +0200 Subject: [PATCH 24/64] Removed warning suppression. --- src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java | 1 - src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java | 1 - .../java/com/hivemq/cli/commands/cli/shell/ConnectST.java | 1 - .../java/com/hivemq/cli/commands/cli/shell/DisconnectST.java | 1 - .../java/com/hivemq/cli/commands/cli/shell/PublishST.java | 1 - .../java/com/hivemq/cli/commands/cli/shell/SubscribeST.java | 1 - 6 files changed, 6 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index 431396a86..4d47095fd 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -35,7 +35,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -@SuppressWarnings("NewClassNamingConvention") class PublishST { private static final @NotNull HiveMQTestContainerExtension hivemq = diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 43f532d4f..aa49e65ec 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -36,7 +36,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -@SuppressWarnings("NewClassNamingConvention") class SubscribeST { private static final @NotNull HiveMQTestContainerExtension hivemq = diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java index 22e51fcc9..fb103ff6a 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -@SuppressWarnings("NewClassNamingConvention") class ConnectST { private static final @NotNull HiveMQTestContainerExtension hivemq = diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java index c24c5a396..408aab9fe 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -@SuppressWarnings("NewClassNamingConvention") class DisconnectST { private static final @NotNull HiveMQTestContainerExtension hivemq = diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java index a9ddcf544..88e60c17d 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -@SuppressWarnings("NewClassNamingConvention") class PublishST { private static final @NotNull HiveMQTestContainerExtension hivemq = diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java index 8e003b7e3..f7ed3f4dc 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -@SuppressWarnings("NewClassNamingConvention") class SubscribeST { private static final @NotNull HiveMQTestContainerExtension hivemq = From bf090e54bfa8c400be5f64d63722385215e2c972 Mon Sep 17 00:00:00 2001 From: gitseti Date: Wed, 5 Oct 2022 12:05:17 +0200 Subject: [PATCH 25/64] System Tests > Refactor tests --- .github/workflows/check.yml | 12 + build.gradle.kts | 1 + .../hivemq/cli/commands/cli/PublishST.java | 87 ++++--- .../hivemq/cli/commands/cli/SubscribeST.java | 57 +++-- .../cli/commands/cli/shell/ConnectST.java | 89 +++++++- .../cli/commands/cli/shell/DisconnectST.java | 24 +- .../cli/commands/cli/shell/PublishST.java | 57 ++--- .../cli/commands/cli/shell/SubscribeST.java | 36 +-- .../com/hivemq/cli/utils/AwaitOutput.java | 63 ++++++ .../cli/utils/CLIShellTestExtension.java | 212 ------------------ .../hivemq/cli/utils/CLITestExtension.java | 125 ----------- .../com/hivemq/cli/utils/CommandConsumer.java | 56 ----- .../com/hivemq/cli/utils/ExecutionResult.java | 42 ++++ .../java/com/hivemq/cli/utils/LogWaiter.java | 82 +++++++ .../java/com/hivemq/cli/utils/MqttCli.java | 84 +++++++ .../com/hivemq/cli/utils/MqttCliShell.java | 126 +++++++++++ .../java/com/hivemq/cli/utils/ProcessIO.java | 162 +++++++++++++ .../hivemq/cli/utils/TimeoutException.java | 32 +++ 18 files changed, 806 insertions(+), 541 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java delete mode 100644 src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java delete mode 100644 src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java delete mode 100644 src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/LogWaiter.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/MqttCli.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 49b8a812d..560ce4904 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -26,3 +26,15 @@ jobs: run: ./gradlew installNativeImageTooling - name: Check run: ./gradlew check --stacktrace + - name: Upload Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: test-results + path: build/test-results/ + - name: Publish Test Report + uses: mikepenz/action-junit-report@v3 + if: always() # always run even if the previous step fails + with: + report_paths: '**/build/test-results/*/TEST-*.xml' + diff --git a/build.gradle.kts b/build.gradle.kts index 723c189d0..bb4d3b48a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -277,6 +277,7 @@ val systemTestRuntimeOnly: Configuration by configurations.getting { dependencies { systemTestImplementation("com.hivemq:hivemq-testcontainer-junit5:${property("hivemq-testcontainer.version")}") systemTestImplementation("org.testcontainers:testcontainers:${property("testcontainers.version")}") + systemTestImplementation("org.awaitility:awaitility:${property("awaitility.version")}") } tasks.named("compileSystemTestJava") { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index e8ff330cd..d3be4ad94 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -16,7 +16,8 @@ package com.hivemq.cli.commands.cli; -import com.hivemq.cli.utils.CLITestExtension; +import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; @@ -27,21 +28,19 @@ import org.junit.jupiter.api.Timeout; import org.testcontainers.utility.DockerImageName; -import java.io.IOException; -import java.util.ArrayList; import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class PublishST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); - private final @NotNull CLITestExtension cliTestExtension = new CLITestExtension(); + private final @NotNull MqttCli mqttCli = new MqttCli(); @BeforeAll static void beforeAll() { @@ -56,17 +55,14 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_publish() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("pub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - publishCommand.add("-t"); - publishCommand.add("test"); - publishCommand.add("-m"); - publishCommand.add("test"); - publishCommand.add("-d"); + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-t", "test", + "-m", "test", + "-d" + ); final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() .identifier("subscriber") @@ -74,45 +70,48 @@ void test_successful_publish() throws Exception { .serverPort(hivemq.getMqttPort()) .buildBlocking(); subscriber.connect(); - final CompletableFuture testReturn = new CompletableFuture<>(); - subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> testReturn.complete(null)).send(); - final Process pub = new ProcessBuilder(publishCommand).start(); + final CountDownLatch receivedPublish = new CountDownLatch(1); + subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> receivedPublish.countDown()).send(); - cliTestExtension.waitForOutputWithTimeout(pub, "received PUBLISH acknowledgement"); - testReturn.get(10, TimeUnit.SECONDS); + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertTrue(receivedPublish.await(10, TimeUnit.SECONDS)); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); + assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); + assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_topic() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("pub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()) + ); - final Process pub = new ProcessBuilder(publishCommand).start(); + final ExecutionResult executionResult = mqttCli.execute(publishCommand); - cliTestExtension.waitForErrorWithTimeout(pub, "Missing required option: '--topic '"); - assertEquals(pub.waitFor(), 2); + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_message() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("pub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - publishCommand.add("-t"); - publishCommand.add("test"); - - final Process pub = new ProcessBuilder(publishCommand).start(); - - cliTestExtension.waitForErrorWithTimeout(pub, Set.of("Error: Missing required argument (specify one of these)")); - assertEquals(pub.waitFor(), 2); + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-t", "test" + ); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m | -m:file )") + || executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m:file= | -m=)")); } } \ No newline at end of file diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 424c147f4..4f281264c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -16,7 +16,9 @@ package com.hivemq.cli.commands.cli; -import com.hivemq.cli.utils.CLITestExtension; +import com.hivemq.cli.utils.AwaitOutput; +import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; @@ -27,21 +29,19 @@ import org.junit.jupiter.api.Timeout; import org.testcontainers.utility.DockerImageName; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SubscribeST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq-ce")); - private final @NotNull CLITestExtension cliTestExtension = new CLITestExtension(); + private final @NotNull MqttCli mqttCli = new MqttCli(); @BeforeAll static void beforeAll() { @@ -56,17 +56,13 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_subscribe() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("sub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - publishCommand.add("-t"); - publishCommand.add("test"); - publishCommand.add("-d"); - - final Process sub = new ProcessBuilder(publishCommand).start(); + final List subscribeCommand = List.of( + "sub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-t", "test", + "-d" + ); final Mqtt5BlockingClient publisher = Mqtt5Client.builder() .identifier("publisher") @@ -75,24 +71,27 @@ void test_successful_subscribe() throws Exception { .buildBlocking(); publisher.connect(); - cliTestExtension.waitForOutputWithTimeout(sub, "received SUBACK"); - final CompletableFuture testReturn = cliTestExtension.waitForOutput(sub, "testReturn"); + final AwaitOutput awaitOutput = mqttCli.executeAsync(subscribeCommand); + + awaitOutput.awaitStdOut("received SUBACK"); + publisher.publishWith().topic("test").payload("testReturn".getBytes(StandardCharsets.UTF_8)).send(); - testReturn.get(10, TimeUnit.SECONDS); + + awaitOutput.awaitStdOut("testReturn"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_subscribe_missing_topic() throws Exception { - final List publishCommand = new ArrayList<>(CLITestExtension.CLI_EXEC); - publishCommand.add("sub"); - publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); - publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); - final Process sub = new ProcessBuilder(publishCommand).start(); - - cliTestExtension.waitForErrorWithTimeout(sub, "Missing required option: '--topic '"); - assertEquals(sub.waitFor(), 2); + final List subscribeCommand = List.of( + "sub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()) + ); + + final ExecutionResult executionResult = mqttCli.execute(subscribeCommand); + + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java index d00736baf..4ff580693 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,7 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; -import java.util.Set; +import java.util.List; import java.util.concurrent.TimeUnit; public class ConnectST { @@ -35,7 +35,7 @@ public class ConnectST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + final MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -49,18 +49,83 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_connect() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); + void test_successful_connect() throws Exception { + final List connectCommand = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-i", "cliTest" + ); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + } + + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_unsuccessful_connect() throws Exception { + final List connectCommand = List.of( + "con", + "-h", "localhost", + "-p", "22", + "-i", "cliTest" + ); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdErr("Connection refused") + .awaitStdOut("mqtt>") + .awaitLog("Connection refused"); + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_successful_connect_no_client_id() throws Exception { + final List connectCommand = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()) + ); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_unsuccessful_connect() { - cliShellTestExtension.executeCommandWithErrorWithTimeout("con -h localhost -p 22 -i cliTest", Set.of( - "Connection refused: localhost/127.0.0.1:22", - "readAddress(..) failed: Connection reset by peer", - "Connection reset")); + void test_successful_connect_session_expiry() throws Exception { + final List connectCommand = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-se", "60", + "-i", "sessionTest", + "--no-cleanStart" + ); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("cleanStart=false") + .awaitLog("sessionExpiryInterval=60") + .awaitLog("received CONNACK") + .awaitLog("sessionPresent=false"); + + mqttCliShell.executeAsync(List.of("dis")) + .awaitStdOut("mqtt>") + .awaitLog("sending DISCONNECT"); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("cleanStart=false") + .awaitLog("sessionExpiryInterval=60") + .awaitLog("received CONNACK") + .awaitLog("sessionPresent=true"); } + } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java index 88db56880..d10a3b24e 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,6 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; +import java.util.List; import java.util.concurrent.TimeUnit; public class DisconnectST { @@ -34,7 +35,7 @@ public class DisconnectST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -48,19 +49,18 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_disconnect() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithTimeout("dis", "mqtt>"); + void test_successful_disconnect() throws Exception { + final List disconnectCommand = List.of("dis"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_unsuccessful_disconnect() { - cliShellTestExtension.executeCommandWithErrorWithTimeout( - "dis", - "Missing required option '--identifier='"); + void test_unsuccessful_disconnect() throws Exception { + final List disconnectCommand = List.of("dis"); + mqttCliShell.executeAsync(disconnectCommand) + .awaitStdErr("Missing required option '--identifier='") + .awaitStdOut("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java index fd584ec1f..3fb742830 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,7 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; -import java.util.Set; +import java.util.List; import java.util.concurrent.TimeUnit; public class PublishST { @@ -35,7 +35,7 @@ public class PublishST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -49,47 +49,38 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_publish() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithTimeout("pub -t test -m test", "cliTest@" + hivemq.getHost() + ">"); - } - - @Test - @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_topic() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithErrorWithTimeout("pub", "Missing required option: '--topic '"); + void test_successful_publish() throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeAsync(publishCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_message() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithErrorWithTimeout("pub -t test", Set.of("Error: Missing required argument (specify one of these)")); + void test_publish_missing_topic() throws Exception { + final List publishCommand = List.of("pub"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeAsync(publishCommand) + .awaitStdErr("Missing required option: '--topic '") + .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_missing_arguments() { - cliShellTestExtension.executeCommandWithErrorWithTimeout("pub", "Unmatched argument at index 0: 'pub'"); + void test_publish_missing_message() throws Exception { + final List publishCommand = List.of("pub", "-t", "test"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeAsync(publishCommand) + .awaitStdErr("Error: Missing required argument (specify one of these)") + .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_empty() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithTimeout("pub -t test -m:empty", "cliTest@" + hivemq.getHost() + ">"); + void test_missing_arguments() throws Exception { + final List publishCommand = List.of("pub"); + mqttCliShell.executeAsync(publishCommand) + .awaitStdErr("Unmatched argument at index 0: 'pub'") + .awaitStdOut("mqtt>"); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java index c9b1d9c04..314995e01 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java @@ -16,7 +16,7 @@ package com.hivemq.cli.commands.cli.shell; -import com.hivemq.cli.utils.CLIShellTestExtension; +import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -26,6 +26,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; +import java.util.List; import java.util.concurrent.TimeUnit; public class SubscribeST { @@ -34,7 +35,7 @@ public class SubscribeST { new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); @RegisterExtension - private final @NotNull CLIShellTestExtension cliShellTestExtension = new CLIShellTestExtension(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @BeforeAll static void beforeAll() { @@ -48,29 +49,28 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_subscribe() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithTimeout("sub -t test", "cliTest@" + hivemq.getHost() + ">"); + void test_successful_subscribe() throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeAsync(subscribeCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_subscribe_missing_topic() { - cliShellTestExtension.executeCommandWithTimeout( - "con -h " + hivemq.getHost() + " -p " + hivemq.getMqttPort() + " -i cliTest", - "cliTest@" + hivemq.getHost() + ">"); - - cliShellTestExtension.executeCommandWithErrorWithTimeout( - "sub", - "Missing required option: '--topic '"); + void test_subscribe_missing_topic() throws Exception{ + final List subscribeCommand = List.of("sub"); + mqttCliShell.connectClient(hivemq); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdErr("Missing required option: '--topic '") + .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_missing_arguments() { - cliShellTestExtension.executeCommandWithErrorWithTimeout("sub", "Unmatched argument at index 0: 'sub'"); + void test_missing_arguments() throws Exception { + final List subscribeCommand = List.of("sub"); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut("mqtt>") + .awaitStdErr("Unmatched argument at index 0: 'sub'"); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java new file mode 100644 index 000000000..1d0758d4a --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Assertions; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class AwaitOutput { + + private final @NotNull ProcessIO processIO; + private final @NotNull String command; + private final @Nullable LogWaiter logWaiter; + + public AwaitOutput(final @NotNull ProcessIO processIO, final @Nullable LogWaiter logWaiter, final @NotNull String command) { + this.processIO = processIO; + this.command = command; + this.logWaiter = logWaiter; + } + + public @NotNull AwaitOutput awaitStdOut(final @NotNull String expectedOutput) { + try { + processIO.awaitStdOut(expectedOutput); + } catch (final TimeoutException timeoutException) { + Assertions.fail(String.format("Command '%s' did not return expected standard output '%s' in time. Actual read standard output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + } + return this; + } + + public @NotNull AwaitOutput awaitStdErr(final @NotNull String expectedOutput) { + try { + processIO.awaitStdErr(expectedOutput); + } catch (final TimeoutException timeoutException) { + Assertions.fail(String.format("Command '%s' did not return expected error output '%s' in time. Actual read error output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + } + return this; + } + + public @NotNull AwaitOutput awaitLog(final @NotNull String expectedLogMessage) { + assertNotNull(logWaiter); + try { + logWaiter.awaitLog(expectedLogMessage); + } catch (final TimeoutException timeoutException) { + Assertions.fail(String.format("Command '%s' did not return expected logfile output '%s' in time. Actual read logfile output: '%s'", command, expectedLogMessage, timeoutException.getActualOutput()), timeoutException); + } + return this; + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java b/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java deleted file mode 100644 index bf855013d..000000000 --- a/src/systemTest/java/com/hivemq/cli/utils/CLIShellTestExtension.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.utils; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -public class CLIShellTestExtension implements BeforeEachCallback, AfterEachCallback { - - public static final @NotNull List CLI_EXEC = - Arrays.stream(Objects.requireNonNull(System.getProperty("cliExec")).split(" ")) - .collect(Collectors.toList()); - - private static final int TIMEOUT = 10; - - private @Nullable Process cliShell; - private final @NotNull CommandConsumer commandConsumer = new CommandConsumer(); - private final @NotNull CommandConsumer errorConsumer = new CommandConsumer(); - - @Override - public void beforeEach(final @NotNull ExtensionContext context) throws Exception { - final ArrayList shellCommand = new ArrayList<>(CLI_EXEC); - shellCommand.add("sh"); - cliShell = new ProcessBuilder(shellCommand).start(); - waitForStartup(cliShell).get(TIMEOUT, TimeUnit.SECONDS); - } - - @Override - public void afterEach(final @NotNull ExtensionContext context) { - if (cliShell == null) { - throw new IllegalStateException(); - } - cliShell.destroy(); - } - - private @NotNull CompletableFuture waitForStartup(final @NotNull Process cliShell) { - final InputStream inputStream = cliShell.getInputStream(); - final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - final BufferedReader bufferedInputReader = new BufferedReader(inputStreamReader); - - final CompletableFuture cliReady = commandConsumer.waitFor("mqtt>"); - final StringBuilder commandBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (!cliReady.isDone()) { - try { - final int inputChar; - inputChar = bufferedInputReader.read(); - - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but command was not found."); - } - commandBuilder.append((char) inputChar); - commandConsumer.accept(commandBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } - - public void executeCommandWithTimeout(final @NotNull String command, final @NotNull String expectedReturn) { - executeCommandWithTimeout(command, Set.of(expectedReturn)); - } - - public @NotNull CompletableFuture executeCommand( - final @NotNull String command, final @NotNull String expectedReturn) { - return executeCommand(command, Set.of(expectedReturn)); - } - - public void executeCommandWithTimeout(final @NotNull String command, final @NotNull Set expectedReturns) { - try { - executeCommand(command, expectedReturns).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture executeCommand( - final @NotNull String command, final @NotNull Set expectedReturns) { - if (cliShell == null) { - throw new IllegalStateException(); - } - - try { - final OutputStream outputStream = cliShell.getOutputStream(); - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); - final BufferedWriter bufferedOutputWriter = new BufferedWriter(outputStreamWriter); - bufferedOutputWriter.write(command); - bufferedOutputWriter.write("\n"); - bufferedOutputWriter.flush(); - } catch (final IOException e) { - throw new RuntimeException(e); - } - - final InputStream inputStream = cliShell.getInputStream(); - final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - final BufferedReader bufferedInputReader = new BufferedReader(inputStreamReader); - - final ArrayList> commandFinished = new ArrayList<>(); - for (final String expectedReturn : expectedReturns) { - commandFinished.add(commandConsumer.waitFor(expectedReturn)); - } - final StringBuilder commandBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (commandFinished.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { - try { - final int inputChar; - inputChar = bufferedInputReader.read(); - - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but command was not found."); - } - commandBuilder.append((char) inputChar); - commandConsumer.accept(commandBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } - - public void executeCommandWithErrorWithTimeout( - final @NotNull String command, final @NotNull String expectedError) { - executeCommandWithErrorWithTimeout(command, Set.of(expectedError)); - } - - public @NotNull CompletableFuture executeCommandWithError( - final @NotNull String command, final @NotNull String expectedError) { - return executeCommandWithError(command, Set.of(expectedError)); - } - - public void executeCommandWithErrorWithTimeout( - final @NotNull String command, final @NotNull Set expectedErrors) { - try { - executeCommandWithError(command, expectedErrors).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture executeCommandWithError( - final @NotNull String command, final @NotNull Set expectedErrors) { - if (cliShell == null) { - throw new IllegalStateException(); - } - - try { - final OutputStream outputStream = cliShell.getOutputStream(); - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); - final BufferedWriter bufferedOutputWriter = new BufferedWriter(outputStreamWriter); - bufferedOutputWriter.write(command); - bufferedOutputWriter.write("\n"); - bufferedOutputWriter.flush(); - } catch (final IOException e) { - throw new RuntimeException(e); - } - - final InputStream errorStream = cliShell.getErrorStream(); - final InputStreamReader errorStreamReader = new InputStreamReader(errorStream, StandardCharsets.UTF_8); - final BufferedReader bufferedErrorReader = new BufferedReader(errorStreamReader); - - final ArrayList> commandFinished = new ArrayList<>(); - for (final String expectedError : expectedErrors) { - commandFinished.add(errorConsumer.waitFor(expectedError)); - } - final StringBuilder errorBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (commandFinished.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { - try { - final int inputChar = bufferedErrorReader.read(); - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but error was not found."); - } - errorBuilder.append((char) inputChar); - errorConsumer.accept(errorBuilder.toString()); - System.out.println(errorBuilder); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } -} diff --git a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java b/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java deleted file mode 100644 index e8344d497..000000000 --- a/src/systemTest/java/com/hivemq/cli/utils/CLITestExtension.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.utils; - -import org.jetbrains.annotations.NotNull; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -public class CLITestExtension { - - public static final @NotNull List CLI_EXEC = - Arrays.stream(Objects.requireNonNull(System.getProperty("cliExec")).split(" ")) - .collect(Collectors.toList()); - - private static final int TIMEOUT = 10; - - private final @NotNull CommandConsumer commandConsumer = new CommandConsumer(); - private final @NotNull CommandConsumer errorConsumer = new CommandConsumer(); - - public void waitForOutputWithTimeout(final @NotNull Process process, final @NotNull String expectedReturn) { - try { - waitForOutput(process, expectedReturn).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture waitForOutput( - final @NotNull Process process, final @NotNull String expectedReturn) { - final InputStream inputStream = process.getInputStream(); - final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - final BufferedReader bufferedInputReader = new BufferedReader(inputStreamReader); - - final CompletableFuture commandReturned = commandConsumer.waitFor(expectedReturn); - final StringBuilder commandBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (!commandReturned.isDone()) { - try { - final int inputChar; - inputChar = bufferedInputReader.read(); - - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but command was not found."); - } - commandBuilder.append((char) inputChar); - commandConsumer.accept(commandBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } - - public void waitForErrorWithTimeout( - final @NotNull Process process, final @NotNull String expectedError) { - waitForErrorWithTimeout(process, Set.of(expectedError)); - } - - public @NotNull CompletableFuture waitForError( - final @NotNull Process process, final @NotNull String expectedError) { - return waitForError(process, Set.of(expectedError)); - } - - public void waitForErrorWithTimeout( - final @NotNull Process process, final @NotNull Set expectedErrors) { - try { - waitForError(process, expectedErrors).get(TIMEOUT, TimeUnit.SECONDS); - } catch (final InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - public @NotNull CompletableFuture waitForError( - final @NotNull Process process, final @NotNull Set expectedErrors) { - final InputStream errorStream = process.getErrorStream(); - final InputStreamReader errorStreamReader = new InputStreamReader(errorStream, StandardCharsets.UTF_8); - final BufferedReader bufferedErrorReader = new BufferedReader(errorStreamReader); - - final ArrayList> commandFinished = new ArrayList<>(); - for (final String expectedError : expectedErrors) { - commandFinished.add(errorConsumer.waitFor(expectedError)); - } - final StringBuilder errorBuilder = new StringBuilder(); - - return CompletableFuture.runAsync(() -> { - while (commandFinished.stream().filter(CompletableFuture::isDone).findAny().isEmpty()) { - try { - final int inputChar = bufferedErrorReader.read(); - if (inputChar == -1) { - throw new IllegalStateException("End of stream was reached, but error was not found."); - } - errorBuilder.append((char) inputChar); - errorConsumer.accept(errorBuilder.toString()); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - }, runnable -> new Thread(runnable).start()); - } -} diff --git a/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java b/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java deleted file mode 100644 index 27c3e2c85..000000000 --- a/src/systemTest/java/com/hivemq/cli/utils/CommandConsumer.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2019-present HiveMQ and the HiveMQ Community - * - * Licensed 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 com.hivemq.cli.utils; - -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; - -public class CommandConsumer implements Consumer { - - private final @NotNull Map> patterns = new ConcurrentHashMap<>(); - private final @NotNull Map> contains = new ConcurrentHashMap<>(); - - @Override - public void accept(final @NotNull String commandLine) { - patterns.forEach((pattern, future) -> { - if (commandLine.trim().matches(pattern)) { - future.complete(null); - } - }); - contains.forEach((command, future) -> { - if (commandLine.contains(command)) { - future.complete(null); - } - }); - } - - public @NotNull CompletableFuture waitForPattern(final @NotNull String pattern) { - final CompletableFuture future = new CompletableFuture<>(); - patterns.put(pattern, future); - return future; - } - - public @NotNull CompletableFuture waitFor(final @NotNull String command) { - final CompletableFuture future = new CompletableFuture<>(); - contains.put(command, future); - return future; - } -} \ No newline at end of file diff --git a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java new file mode 100644 index 000000000..bdb4f27a2 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; + +public class ExecutionResult { + private final int exitCode; + private final @NotNull String standardOutput; + private final @NotNull String errorOutput; + + public ExecutionResult(final int exitCode, final @NotNull String standardOutput, final @NotNull String errorOutput) { + this.exitCode = exitCode; + this.standardOutput = standardOutput; + this.errorOutput = errorOutput; + } + + public int getExitCode() { + return exitCode; + } + + public @NotNull String getStandardOutput() { + return standardOutput; + } + + public @NotNull String getErrorOutput() { + return errorOutput; + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/LogWaiter.java b/src/systemTest/java/com/hivemq/cli/utils/LogWaiter.java new file mode 100644 index 000000000..2ff1e8427 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/LogWaiter.java @@ -0,0 +1,82 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.awaitility.core.ConditionTimeoutException; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import static org.awaitility.Awaitility.await; + +public class LogWaiter { + + private final @NotNull File logFile; + private final @NotNull AtomicLong filePosition; + + public LogWaiter(final @NotNull File logFile) { + this.logFile = logFile; + this.filePosition = new AtomicLong(0); + } + + public void awaitLog(final @NotNull String expectedLogMessage) throws TimeoutException { + + final AtomicReference readLog = new AtomicReference<>(); + + try { + await().until(() -> { + final StringBuilder readChars = new StringBuilder(); + try { + final FileReader fileReader = new FileReader(logFile, StandardCharsets.UTF_8); + fileReader.skip(filePosition.get()); + + while (true) { + for (int i = 0; i < expectedLogMessage.length(); i++) { + final int readChar = fileReader.read(); + readChars.append((char) readChar); + + if (readChar == -1) { + return false; + } + + if (expectedLogMessage.charAt(i) != readChar) { + break; + } + + + if (i == expectedLogMessage.length() - 1) { + filePosition.set(filePosition.get() + readChars.length()); + return true; + } + } + } + } finally { + readLog.set(readChars.toString()); + } + + }); + } catch (final ConditionTimeoutException e) { + throw new TimeoutException(e, readLog.get()); + } + + } + +} + diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java new file mode 100644 index 000000000..0d1c7e7f2 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java @@ -0,0 +1,84 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MqttCli { + + // See 'systemTest' and 'systemTestNative' in build.gradle.kts + // Depending on the task used, the property cliExec either contains the absolute path to the jar or the native image executable + public static final @NotNull List CLI_EXEC = + Arrays.stream(Objects.requireNonNull(System.getProperty("cliExec")).split(" ")) + .collect(Collectors.toUnmodifiableList()); + + /** + * Executes a mqtt-cli command in blocking manner. This method should be used for all mqtt-cli commands which + * exit the cli with an exit code. + * @param command the command to execute with the mqtt cli + * @return an {@link ExecutionResult} which contains the std-output, err-ouput and exit-code of the command's execution + * @throws IOException when an error occurred while starting the process or reading its output + * @throws InterruptedException when the process was interrupted + */ + public @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { + final List fullCommand = new ArrayList<>(CLI_EXEC); + assertTrue(fullCommand.addAll(command)); + + final Process process = new ProcessBuilder(fullCommand).start(); + + final int exitCode = process.waitFor(); + + final String stdOut = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + final String stdErr = new String(process.getErrorStream().readAllBytes(), StandardCharsets.UTF_8); + if (!stdOut.isEmpty()) { + System.out.println(stdOut); + } + if (!stdErr.isEmpty()) { + System.err.println(stdErr); + } + + return new ExecutionResult(exitCode, stdOut, stdErr); + } + + /** + * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not + * exit the process like the subscribe command. + * @param command the command to execute with the mqtt cli + * @return an {@link AwaitOutput} which can be used to wait for std-out std-err messages + * @throws IOException when an error occurred while starting the process + */ + public @NotNull AwaitOutput executeAsync(final @NotNull List command) + throws IOException { + final List fullCommand = new ArrayList<>(CLI_EXEC); + assertTrue(fullCommand.addAll(command)); + + final Process process = new ProcessBuilder(fullCommand).start(); + final ProcessIO processIO = ProcessIO.startReading(process); + + return new AwaitOutput(processIO, null, String.join(" ", command)); + } + +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java new file mode 100644 index 000000000..4b835dfe8 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -0,0 +1,126 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { + + private ProcessIO processIO; + private Process process; + private LogWaiter logWaiter; + + @Override + public void beforeEach(final @NotNull ExtensionContext context) throws Exception { + + // Setup the mqtt-cli home folder for logging, etc. + final Path homeDir = Files.createTempDirectory("mqtt-cli-home"); + homeDir.toFile().deleteOnExit(); + + // Start and await the start of the shell + this.process = startShellMode(homeDir); + this.processIO = ProcessIO.startReading(process); + new AwaitOutput(processIO, null, String.join(" ",getShellCommand(homeDir))).awaitStdOut("mqtt>"); + + // We can only initialize the logger after starting up the shell because the startup initializes the logfile + this.logWaiter = setupLogWaiter(homeDir); + } + + @Override + public void afterEach(final @NotNull ExtensionContext context) { + if (process.isAlive()) { + process.destroy(); + } + } + + private @NotNull Process startShellMode(final @NotNull Path homeDir) throws IOException { + final List shellCommand = getShellCommand(homeDir); + return new ProcessBuilder(shellCommand).start(); + } + + private @NotNull List getShellCommand(final Path homeDir) { + // Set system property 'user.home' to the temp home directory, so that the cli tests does not use the default home folder + final ArrayList shellCommand = new ArrayList<>(CLI_EXEC); + final String homeSystemProperty = String.format("-Duser.home=%s", homeDir.toAbsolutePath()); + if (shellCommand.contains("-jar")) { + // normal java -jar execution + final int index = shellCommand.indexOf("-jar"); + shellCommand.add(index + 1, homeSystemProperty); + } else { + // Graal execution + shellCommand.add(homeSystemProperty); + } + shellCommand.add("shell"); + shellCommand.add("-l"); + return shellCommand; + } + + private @NotNull LogWaiter setupLogWaiter(final @NotNull Path homeDir) { + final File logFolder = homeDir.resolve(".mqtt-cli/logs").toFile(); + final File[] logFiles = logFolder.listFiles(); + assertNotNull(logFiles); + assertEquals(1, logFiles.length); + return new LogWaiter(logFiles[0]); + } + + /** + * Connects a mqtt-client and awaits the successful output statements on std-out and in the logfile. + * + * @param hivemq the HiveMQ container to which the client should connect + * @throws IOException when the cli command to connect could not be written to the shell + */ + public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) throws IOException { + final List connectCommand = + List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-i", "cliTest"); + + + final AwaitOutput awaitOutput = + executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("received CONNACK"); + } + + /** + * Executes a mqtt-cli command asynchronously in the shell. + * + * @param command the command to write and execute in the shell. + * @return an {@link AwaitOutput} which can be used to await std-output, err-output and logfile-messages of the + * cli. + * @throws IOException when the cli command could not be written to the shell + */ + public @NotNull AwaitOutput executeAsync(final @NotNull List command) throws IOException { + final String fullCommand = String.join(" ", command); + + processIO.writeMsg(fullCommand); + return new AwaitOutput(processIO, logWaiter, fullCommand); + } + +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java new file mode 100644 index 000000000..c9177a4e4 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java @@ -0,0 +1,162 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.awaitility.core.ConditionTimeoutException; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.awaitility.Awaitility.await; + +public class ProcessIO { + + private final @NotNull BufferedWriter stdoutWriter; + private final @NotNull StringBuilder processStdOut; + private final @NotNull StringBuilder processStdErr; + private final @NotNull BufferedWriter processOutputWriter; + private final @NotNull AtomicInteger processStdOutMarker; + private final @NotNull AtomicInteger processStdErrMarker; + + private ProcessIO( + final @NotNull BufferedWriter stdoutWriter, + final @NotNull StringBuilder processStdOut, + final @NotNull StringBuilder processStdErr, + final @NotNull BufferedWriter processOutputWriter) { + this.stdoutWriter = stdoutWriter; + this.processStdOut = processStdOut; + this.processStdErr = processStdErr; + this.processOutputWriter = processOutputWriter; + this.processStdOutMarker = new AtomicInteger(0); + this.processStdErrMarker = new AtomicInteger(0); + } + + public static @NotNull ProcessIO startReading(final @NotNull Process process) { + + final StringBuilder processStdOut = new StringBuilder(); + final StringBuilder processErrOut = new StringBuilder(); + final BufferedWriter processOutputWriter = + new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); + + /* Startup std-out writer */ + final InputStream processInputStream = process.getInputStream(); + final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)); + + CompletableFuture.runAsync(() -> { + try { + int previousChar = '\0'; + while (true) { + final int readChar = processInputStream.read(); + + if (readChar == -1) { + return; + } + + writer.write(readChar); + processStdOut.append((char) readChar); + + if ((previousChar == '>' && readChar == ' ') || readChar == '\n') { + writer.flush(); + } + + previousChar = readChar; + } + } catch (final Exception ex) { + ex.printStackTrace(); + } + }); + + /* Startup std-err writer */ + final InputStream processErrorStream = process.getErrorStream(); + final BufferedWriter errorWriter = + new BufferedWriter(new OutputStreamWriter(System.err, StandardCharsets.UTF_8)); + + CompletableFuture.runAsync(() -> { + try { + while (true) { + final int readChar = processErrorStream.read(); + + if (readChar == -1) { + return; + } + + errorWriter.write(readChar); + processErrOut.append((char) readChar); + + if (readChar == '\n') { + errorWriter.flush(); + } + } + } catch (final Exception ex) { + ex.printStackTrace(); + } + }); + + return new ProcessIO(writer, processStdOut, processErrOut, processOutputWriter); + } + + public void awaitStdOut(final @NotNull String stdOutMessage) throws TimeoutException { + try { + await().until(() -> { + final int index = processStdOut.indexOf(stdOutMessage, processStdOutMarker.get()); + + if (index == -1) { + return false; + } + + processStdOutMarker.set(index + stdOutMessage.length()); + return true; + }); + } catch (final @NotNull ConditionTimeoutException timeoutException) { + throw new TimeoutException(timeoutException, processStdOut.substring(processStdOutMarker.get())); + } + } + + public void awaitStdErr(final @NotNull String stdErrMessage) throws TimeoutException { + try { + await().until(() -> { + final int index = processStdErr.indexOf(stdErrMessage, processStdErrMarker.get()); + + if (index == -1) { + return false; + } + + processStdErrMarker.set(index + stdErrMessage.length()); + return true; + }); + } catch (final ConditionTimeoutException timeoutException) { + throw new TimeoutException(timeoutException, processStdErr.substring(processStdErrMarker.get())); + } + + } + + public void writeMsg(final @NotNull String message) throws IOException { + stdoutWriter.write(message); + stdoutWriter.write('\n'); + stdoutWriter.flush(); + + processOutputWriter.write(message); + processOutputWriter.write('\n'); + processOutputWriter.flush(); + } + +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java b/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java new file mode 100644 index 000000000..a67fcf771 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; + +public class TimeoutException extends Exception { + + private final @NotNull String actualOutput; + + public TimeoutException(final @NotNull Throwable cause, final @NotNull String actualOutput) { + super(cause); + this.actualOutput = actualOutput; + } + + public @NotNull String getActualOutput() { + return actualOutput; + } +} From a5bfd463ea236cbc0c0fd10de6deb303128cd4b0 Mon Sep 17 00:00:00 2001 From: gitseti Date: Wed, 5 Oct 2022 13:49:08 +0200 Subject: [PATCH 26/64] System Tests > Rename shell tests --- .../commands/cli/shell/{ConnectST.java => ShellConnectST.java} | 2 +- .../cli/shell/{DisconnectST.java => ShellDisconnectST.java} | 2 +- .../commands/cli/shell/{PublishST.java => ShellPublishST.java} | 2 +- .../cli/shell/{SubscribeST.java => ShellSubscribeST.java} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/systemTest/java/com/hivemq/cli/commands/cli/shell/{ConnectST.java => ShellConnectST.java} (99%) rename src/systemTest/java/com/hivemq/cli/commands/cli/shell/{DisconnectST.java => ShellDisconnectST.java} (98%) rename src/systemTest/java/com/hivemq/cli/commands/cli/shell/{PublishST.java => ShellPublishST.java} (99%) rename src/systemTest/java/com/hivemq/cli/commands/cli/shell/{SubscribeST.java => ShellSubscribeST.java} (98%) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java index 4ff580693..956e4646a 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class ConnectST { +public class ShellConnectST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java index d10a3b24e..4815e9d04 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/DisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class DisconnectST { +public class ShellDisconnectST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java index 3fb742830..43ac1e006 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class PublishST { +public class ShellPublishST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java index 314995e01..aeeabdce1 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class SubscribeST { +public class ShellSubscribeST { private static final @NotNull HiveMQTestContainerExtension hivemq = new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); From 13bb070956bdbb3c85878b25305ddab57eeaabbe Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 6 Oct 2022 10:18:29 +0200 Subject: [PATCH 27/64] System Tests > Add tests --- .../commands/cli/shell/ShellConnectST.java | 210 +++++++++++++++--- .../com/hivemq/cli/utils/MqttCliShell.java | 8 +- 2 files changed, 179 insertions(+), 39 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java index 956e4646a..4cf2dd72d 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java @@ -16,6 +16,7 @@ package com.hivemq.cli.commands.cli.shell; +import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; @@ -24,6 +25,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.utility.DockerImageName; import java.util.List; @@ -49,13 +52,26 @@ static void afterAll() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_connect() throws Exception { + void whenHelpOptionIsUsed_thenUsageHelpIsDisplayed() throws Exception { + final List connectCommand = List.of("con", "--help"); + + mqttCliShell.executeAsync(connectCommand).awaitStdOut("Usage").awaitStdOut("Options").awaitStdOut("mqtt>"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(strings = {"3", "5"}) + void whenConnect_thenConnectIsSuccess(final @NotNull String mqttVersion) throws Exception { final List connectCommand = List.of( "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-i", "cliTest" - ); + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-i", + "cliTest", + "-V", + mqttVersion); mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) @@ -63,16 +79,12 @@ void test_successful_connect() throws Exception { .awaitLog("received CONNACK"); } - - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_unsuccessful_connect() throws Exception { - final List connectCommand = List.of( - "con", - "-h", "localhost", - "-p", "22", - "-i", "cliTest" - ); + @ValueSource(strings = {"3", "5"}) + void whenWrongPortIsUsed_thenConnectIsFailure(final @NotNull String mqttVersion) throws Exception { + final List connectCommand = + List.of("con", "-h", hivemq.getHost(), "-p", "22", "-V", mqttVersion, "-i", "cliTest"); mqttCliShell.executeAsync(connectCommand) .awaitStdErr("Connection refused") @@ -80,52 +92,182 @@ void test_unsuccessful_connect() throws Exception { .awaitLog("Connection refused"); } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_connect_no_client_id() throws Exception { + @ValueSource(strings = {"3", "5"}) + void whenWrongHostIsUsed_thenConnectIsFailure(final @NotNull String mqttVersion) throws Exception { final List connectCommand = List.of( "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()) - ); + "-h", + "unreachable-host", + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + mqttVersion, + "-i", + "cliTest"); mqttCliShell.executeAsync(connectCommand) + .awaitStdErr("nodename nor servname provided, or not known") + .awaitStdOut("mqtt>") + .awaitLog("nodename nor servname provided, or not known"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(strings = {"3", "5"}) + void whenNoClientIdIsUsed_thenConnectIsSuccess(final String mqttVersion) throws Exception { + final List connectCommand = + List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-V", mqttVersion); + + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); + + if (mqttVersion.equals("5")) { + awaitOutput.awaitLog("assignedClientIdentifier="); + } + } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_connect_session_expiry() throws Exception { + @ValueSource(strings = {"5"}) + void whenSessionExpiryIsUsed_thenSessionIsUsed(final @NotNull String mqttVersion) throws Exception { final List connectCommand = List.of( "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-se", "60", - "-i", "sessionTest", - "--no-cleanStart" - ); + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + mqttVersion, + "-se", + "120", + "-i", + "sessionTest", + "--no-cleanStart"); mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") - .awaitLog("cleanStart=false") - .awaitLog("sessionExpiryInterval=60") + .awaitLog("sessionExpiryInterval=120") .awaitLog("received CONNACK") .awaitLog("sessionPresent=false"); - mqttCliShell.executeAsync(List.of("dis")) - .awaitStdOut("mqtt>") - .awaitLog("sending DISCONNECT"); + mqttCliShell.executeAsync(List.of("dis")).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") - .awaitLog("cleanStart=false") - .awaitLog("sessionExpiryInterval=60") + .awaitLog("sessionExpiryInterval=120") .awaitLog("received CONNACK") .awaitLog("sessionPresent=true"); } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(strings = {"3"}) + void whenIdentifierPrefixIsUsed_thenAssignedClientIdStartsWithPrefix(final @NotNull String mqttVersion) + throws Exception { + final List connectCommand = List.of( + "con", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + mqttVersion, + "-ip", + "myPrefix"); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut("myPrefix") + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(strings = {"5"}) + void whenUserPropertyIsUsed_thenConnectSentWithUserProperties(final @NotNull String mqttVersion) throws Exception { + final List connectCommand = List.of( + "con", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-i", + "cliTest", + "-V", + mqttVersion, + "-up", + "key1=value1", + "-up", + "key2=value2"); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("userProperties=[(key1, value1), (key2, value2)]") + .awaitLog("received CONNACK"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(strings = {"3", "5"}) + void whenKeepAliveIsUsed_thenConnectContainsKeepAlive(final @NotNull String mqttVersion) throws Exception { + final List connectCommand = List.of( + "con", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-i", + "cliTest", + "-V", + mqttVersion, + "-k", + "60"); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("keepAlive=60") + .awaitLog("received CONNACK"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(strings = {"3", "5"}) + void test_rcvMax(final @NotNull String mqttVersion) throws Exception { + final List connectCommand = List.of( + "con", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-i", + "cliTest", + "-V", + mqttVersion, + "--rcvMax", + "60"); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT"); + + if (mqttVersion.equals("3")) { + awaitOutput.awaitStdErr("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); + } else { + awaitOutput.awaitLog("receiveMaximum=60"); + } + + awaitOutput.awaitLog("received CONNACK"); + + } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index 4b835dfe8..bc30a07b9 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -49,7 +49,7 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception // Start and await the start of the shell this.process = startShellMode(homeDir); this.processIO = ProcessIO.startReading(process); - new AwaitOutput(processIO, null, String.join(" ",getShellCommand(homeDir))).awaitStdOut("mqtt>"); + new AwaitOutput(processIO, null, String.join(" ", getShellCommand(homeDir))).awaitStdOut("mqtt>"); // We can only initialize the logger after starting up the shell because the startup initializes the logfile this.logWaiter = setupLogWaiter(homeDir); @@ -57,9 +57,7 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception @Override public void afterEach(final @NotNull ExtensionContext context) { - if (process.isAlive()) { - process.destroy(); - } + process.destroyForcibly(); } private @NotNull Process startShellMode(final @NotNull Path homeDir) throws IOException { @@ -105,7 +103,7 @@ public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) th final AwaitOutput awaitOutput = executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); - awaitOutput.awaitLog("received CONNACK"); + awaitOutput.awaitLog("received CONNACK"); } /** From 92ee4d73f6f11ab723e64f0da112260bde3761d1 Mon Sep 17 00:00:00 2001 From: gitseti Date: Thu, 6 Oct 2022 17:22:38 +0200 Subject: [PATCH 28/64] System Tests > Add orphan process destroying for orphan shell processes --- build.gradle.kts | 2 ++ .../com/hivemq/cli/utils/MqttCliShell.java | 24 +++++++++++++++++- .../java/com/hivemq/cli/utils/ProcessIO.java | 18 +++---------- .../resources/OrphanProcessDestroyer.java | 25 +++++++++++++++++++ 4 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 src/systemTest/resources/OrphanProcessDestroyer.java diff --git a/build.gradle.kts b/build.gradle.kts index bb4d3b48a..ad2e93943 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -299,6 +299,7 @@ val systemTest by tasks.registering(Test::class) { dependsOn(tasks.shadowJar) systemProperties["cliExec"] = javaLauncher.get().executablePath.asFile.absolutePath + " -jar " + tasks.shadowJar.map { it.outputs.files.singleFile }.get() + systemProperties["java"] = javaLauncher.get().executablePath.asFile.absolutePath } val systemTestNative by tasks.registering(Test::class) { @@ -314,6 +315,7 @@ val systemTestNative by tasks.registering(Test::class) { dependsOn(tasks.nativeCompile) systemProperties["cliExec"] = tasks.nativeCompile.map { it.outputs.files.singleFile }.get().resolve(project.name).absolutePath + systemProperties["java"] = javaLauncher.get().executablePath.asFile.absolutePath testLogging { showCauses = true showExceptions = true diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index bc30a07b9..943ff658f 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -16,6 +16,7 @@ package com.hivemq.cli.utils; +import com.google.common.io.Resources; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.extension.AfterEachCallback; @@ -38,6 +39,7 @@ public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { private ProcessIO processIO; private Process process; private LogWaiter logWaiter; + private Process orphanProcessDestroyer; @Override public void beforeEach(final @NotNull ExtensionContext context) throws Exception { @@ -48,6 +50,7 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception // Start and await the start of the shell this.process = startShellMode(homeDir); + this.orphanProcessDestroyer = startOrphanProcessDestroyer(process); this.processIO = ProcessIO.startReading(process); new AwaitOutput(processIO, null, String.join(" ", getShellCommand(homeDir))).awaitStdOut("mqtt>"); @@ -55,9 +58,29 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception this.logWaiter = setupLogWaiter(homeDir); } + private @NotNull Process startOrphanProcessDestroyer(final @NotNull Process childProcess) throws IOException { + // We start the ProcessGarbageCollector which sole job is to destroy the childProcess, meaning the mqtt-cli shell, + // when the jvm process exited + final long jvmProcessId = ProcessHandle.current().pid(); + final List processKillerCommand = List.of( + System.getProperty("java"), + Resources.getResource("OrphanProcessDestroyer.java").getPath(), + String.valueOf(jvmProcessId), + String.valueOf(childProcess.pid()) + ); + final Process processKillerProcess = new ProcessBuilder(processKillerCommand).start(); + + // Wait until the process prints X, which means that the process garbage collector has registered the + final int readChar = processKillerProcess.getInputStream().read(); + assertEquals('X', readChar); + + return processKillerProcess; + } + @Override public void afterEach(final @NotNull ExtensionContext context) { process.destroyForcibly(); + orphanProcessDestroyer.destroyForcibly(); } private @NotNull Process startShellMode(final @NotNull Path homeDir) throws IOException { @@ -116,7 +139,6 @@ public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) th */ public @NotNull AwaitOutput executeAsync(final @NotNull List command) throws IOException { final String fullCommand = String.join(" ", command); - processIO.writeMsg(fullCommand); return new AwaitOutput(processIO, logWaiter, fullCommand); } diff --git a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java index c9177a4e4..1489fa249 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java @@ -64,13 +64,8 @@ private ProcessIO( CompletableFuture.runAsync(() -> { try { int previousChar = '\0'; - while (true) { - final int readChar = processInputStream.read(); - - if (readChar == -1) { - return; - } - + int readChar; + while ((readChar = processInputStream.read()) != -1) { writer.write(readChar); processStdOut.append((char) readChar); @@ -92,13 +87,8 @@ private ProcessIO( CompletableFuture.runAsync(() -> { try { - while (true) { - final int readChar = processErrorStream.read(); - - if (readChar == -1) { - return; - } - + int readChar; + while ((readChar = processErrorStream.read()) != -1) { errorWriter.write(readChar); processErrOut.append((char) readChar); diff --git a/src/systemTest/resources/OrphanProcessDestroyer.java b/src/systemTest/resources/OrphanProcessDestroyer.java new file mode 100644 index 000000000..b67e6a6eb --- /dev/null +++ b/src/systemTest/resources/OrphanProcessDestroyer.java @@ -0,0 +1,25 @@ +package com.hivemq.cli.utils; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class ProcessKiller { + + public static void main(final String[] args) + throws ExecutionException, InterruptedException, TimeoutException { + final String jvmProcessId = args[0]; + final String childProcessId = args[1]; + final ProcessHandle jvmProcess = ProcessHandle.of(Long.parseLong(jvmProcessId)).get(); + final ProcessHandle childProcess = ProcessHandle.of(Long.parseLong(childProcessId)).get(); + final CompletableFuture future = + jvmProcess.onExit().whenComplete((processHandle, throwable) -> { + childProcess.destroyForcibly(); + System.exit(0); + }); + System.out.println('X'); + future.get(300, TimeUnit.SECONDS); + } +} + From 8f42e9e4e85cfa87a7740fa6548592f2c96d778b Mon Sep 17 00:00:00 2001 From: gitseti Date: Fri, 7 Oct 2022 17:04:49 +0200 Subject: [PATCH 29/64] System Tests > Add shell-connect tests --- build.gradle.kts | 1 + gradle.properties | 1 + .../commands/cli/shell/ShellConnectST.java | 476 ++++++++++++++---- .../hivemq/cli/utils/ConnectAssertion.java | 148 ++++++ .../java/com/hivemq/cli/utils/HiveMQ.java | 123 +++++ .../com/hivemq/cli/utils/MqttCliShell.java | 28 +- ...stroyer.java => OrphanCleanupProcess.java} | 15 +- 7 files changed, 672 insertions(+), 120 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java rename src/systemTest/resources/{OrphanProcessDestroyer.java => OrphanCleanupProcess.java} (59%) diff --git a/build.gradle.kts b/build.gradle.kts index ad2e93943..273b282e3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -278,6 +278,7 @@ dependencies { systemTestImplementation("com.hivemq:hivemq-testcontainer-junit5:${property("hivemq-testcontainer.version")}") systemTestImplementation("org.testcontainers:testcontainers:${property("testcontainers.version")}") systemTestImplementation("org.awaitility:awaitility:${property("awaitility.version")}") + systemTestImplementation("com.hivemq:hivemq-community-edition-embedded:${property("hivemq-community-edition-embedded.version")}") } tasks.named("compileSystemTestJava") { diff --git a/gradle.properties b/gradle.properties index 861c739ca..d2c7e10d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,6 +34,7 @@ system-exit.version=1.1.2 # integration test dependencies # hivemq-testcontainer.version=2.0.0 +hivemq-community-edition-embedded.version=2022.1 testcontainers.version=1.17.3 awaitility.version=4.2.0 # diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java index 4cf2dd72d..d4bcd93c4 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java @@ -16,86 +16,85 @@ package com.hivemq.cli.commands.cli.shell; +import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.AwaitOutput; +import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; +import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; +import com.hivemq.extension.sdk.api.services.builder.Builders; +import com.hivemq.extension.sdk.api.services.builder.WillPublishBuilder; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.testcontainers.utility.DockerImageName; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + public class ShellConnectST { - private static final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); + @RegisterExtension + static HiveMQ hivemq = new HiveMQ(); @RegisterExtension final MqttCliShell mqttCliShell = new MqttCliShell(); - @BeforeAll - static void beforeAll() { - hivemq.start(); - } - - @AfterAll - static void afterAll() { - hivemq.stop(); - } - @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void whenHelpOptionIsUsed_thenUsageHelpIsDisplayed() throws Exception { final List connectCommand = List.of("con", "--help"); - mqttCliShell.executeAsync(connectCommand).awaitStdOut("Usage").awaitStdOut("Options").awaitStdOut("mqtt>"); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"3", "5"}) - void whenConnect_thenConnectIsSuccess(final @NotNull String mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", - "-h", - hivemq.getHost(), - "-p", - String.valueOf(hivemq.getMqttPort()), - "-i", - "cliTest", - "-V", - mqttVersion); + @ValueSource(chars = {'3', '5'}) + void whenConnect_thenConnectIsSuccess(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); + + final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(toVersion(mqttVersion))); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"3", "5"}) - void whenWrongPortIsUsed_thenConnectIsFailure(final @NotNull String mqttVersion) throws Exception { + @ValueSource(chars = {'3', '5'}) + void whenWrongPortIsUsed_thenConnectIsFailure(final char mqttVersion) throws Exception { final List connectCommand = - List.of("con", "-h", hivemq.getHost(), "-p", "22", "-V", mqttVersion, "-i", "cliTest"); + List.of("con", "-h", hivemq.getHost(), "-p", "22", "-V", String.valueOf(mqttVersion), "-i", "cliTest"); mqttCliShell.executeAsync(connectCommand) .awaitStdErr("Connection refused") .awaitStdOut("mqtt>") .awaitLog("Connection refused"); + + assertEquals(0, hivemq.getConnectPackets().size()); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"3", "5"}) - void whenWrongHostIsUsed_thenConnectIsFailure(final @NotNull String mqttVersion) throws Exception { + @ValueSource(chars = {'3', '5'}) + void whenWrongHostIsUsed_thenConnectIsFailure(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", @@ -103,7 +102,7 @@ void whenWrongHostIsUsed_thenConnectIsFailure(final @NotNull String mqttVersion) "-p", String.valueOf(hivemq.getMqttPort()), "-V", - mqttVersion, + String.valueOf(mqttVersion), "-i", "cliTest"); @@ -111,31 +110,159 @@ void whenWrongHostIsUsed_thenConnectIsFailure(final @NotNull String mqttVersion) .awaitStdErr("nodename nor servname provided, or not known") .awaitStdOut("mqtt>") .awaitLog("nodename nor servname provided, or not known"); + + assertEquals(0, hivemq.getConnectPackets().size()); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"3", "5"}) - void whenNoClientIdIsUsed_thenConnectIsSuccess(final String mqttVersion) throws Exception { - final List connectCommand = - List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-V", mqttVersion); + @ValueSource(chars = {'3', '5'}) + void whenNoCleanStartIsUsed_thenConnectCleanStartIsFalse(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--no-cleanStart"); + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setCleanStart(false); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenReceiveMaximumIsUsed_thenConnectContainsReceiveMaximum(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--rcvMax"); + connectCommand.add("500"); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - if (mqttVersion.equals("5")) { - awaitOutput.awaitLog("assignedClientIdentifier="); + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); } + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setReceiveMaximum(500); + } + }); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"5"}) - void whenSessionExpiryIsUsed_thenSessionIsUsed(final @NotNull String mqttVersion) throws Exception { + @ValueSource(chars = {'3', '5'}) + void whenMaxPacketSizeIsUsed_thenConnectPacketContainsMaxPacketSize(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--maxPacketSize"); + connectCommand.add("500"); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setMaximumPacketSize(500); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenTopicAliasMaxIsUsed_thenConnectContainsTopicAliasMax(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--topicAliasMax"); + connectCommand.add("5"); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setTopicAliasMaximum(5); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenRequestProblemInformationIsUSed_thenConnectContainsRequestProblemInformation(final char mqttVersion) + throws IOException { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--no-reqProblemInfo"); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr( + "Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestProblemInformation(false); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenRequestResponseInformationIsUsed_thenConnectContainsRequestResponseInformation(final char mqttVersion) + throws IOException { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--reqResponseInfo"); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr( + "Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestResponseInformation(true); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenNoClientIdIsUsed_thenConnectIsSuccess(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", @@ -143,7 +270,148 @@ void whenSessionExpiryIsUsed_thenSessionIsUsed(final @NotNull String mqttVersion "-p", String.valueOf(hivemq.getMqttPort()), "-V", - mqttVersion, + String.valueOf(mqttVersion)); + + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + if (mqttVersion == '5') { + awaitOutput.awaitLog("assignedClientIdentifier="); + } + + assertEquals(1, hivemq.getConnectPackets().size()); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenUserNameIsUsed_thenConnectPacketContainsUserName(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-u"); + connectCommand.add("username"); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenPasswordIsUsed_thenConnectPacketContainsPassword(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-pw"); + connectCommand.add("password"); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + } else { + awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void whenWillIsUsed_thenConnectContainsWill(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-Wt"); + connectCommand.add("test-will-topic"); + connectCommand.add("-Wm"); + connectCommand.add("will-message"); + connectCommand.add("-Wq"); + connectCommand.add("2"); + connectCommand.add("-Wr"); + connectCommand.add("-We"); + connectCommand.add("120"); + connectCommand.add("-Wd"); + connectCommand.add("180"); + connectCommand.add("-Wpf"); + connectCommand.add(PayloadFormatIndicator.UTF_8.name()); + connectCommand.add("-Wct"); + connectCommand.add("content-type"); + connectCommand.add("-Wrt"); + connectCommand.add("will-response-topic"); + connectCommand.add("-Wup"); + connectCommand.add("key1=value1"); + connectCommand.add("-Wup"); + connectCommand.add("key2=value2"); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitLog("Will Message Expiry was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Will Payload Format was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Will Delay Interval was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Will Content Type was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Will Response Topic was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + final WillPublishBuilder expectedWillBuilder = Builders.willPublish() + .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) + .topic("test-will-topic") + .qos(Qos.EXACTLY_ONCE) + .messageExpiryInterval(4294967295L) + .retain(true); + + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setWillPublish(expectedWillBuilder.build()); + }); + + } else { + awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + final WillPublishBuilder expectedWillBuilder = Builders.willPublish() + .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) + .topic("test-will-topic") + .qos(Qos.EXACTLY_ONCE) + .retain(true) + .payloadFormatIndicator(PayloadFormatIndicator.UNSPECIFIED) + .messageExpiryInterval(120) + .willDelay(180) + .payloadFormatIndicator(PayloadFormatIndicator.UTF_8) + .contentType("content-type") + .responseTopic("will-response-topic") + .userProperty("key1", "value1") + .userProperty("key2", "value2"); + + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setWillPublish(expectedWillBuilder.build()); + }); + } + + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void whenSessionExpiryIsUsed_thenSessionIsUsed() throws Exception { + final List connectCommand = List.of( + "con", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), "-se", "120", "-i", @@ -157,6 +425,13 @@ void whenSessionExpiryIsUsed_thenSessionIsUsed(final @NotNull String mqttVersion .awaitLog("received CONNACK") .awaitLog("sessionPresent=false"); + final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); + assertConnectPacket(connectPacket1, connectAssertion -> { + connectAssertion.setClientId("sessionTest"); + connectAssertion.setCleanStart(false); + connectAssertion.setSessionExpiryInterval(120); + }); + mqttCliShell.executeAsync(List.of("dis")).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); mqttCliShell.executeAsync(connectCommand) @@ -165,13 +440,18 @@ void whenSessionExpiryIsUsed_thenSessionIsUsed(final @NotNull String mqttVersion .awaitLog("sessionExpiryInterval=120") .awaitLog("received CONNACK") .awaitLog("sessionPresent=true"); + + final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); + assertConnectPacket(connectPacket2, connectAssertion -> { + connectAssertion.setClientId("sessionTest"); + connectAssertion.setCleanStart(false); + connectAssertion.setSessionExpiryInterval(120); + }); } - @ParameterizedTest + @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"3"}) - void whenIdentifierPrefixIsUsed_thenAssignedClientIdStartsWithPrefix(final @NotNull String mqttVersion) - throws Exception { + void whenIdentifierPrefixIsUsed_thenAssignedClientIdStartsWithPrefix() throws Exception { final List connectCommand = List.of( "con", "-h", @@ -179,7 +459,7 @@ void whenIdentifierPrefixIsUsed_thenAssignedClientIdStartsWithPrefix(final @NotN "-p", String.valueOf(hivemq.getMqttPort()), "-V", - mqttVersion, + "3", "-ip", "myPrefix"); @@ -190,36 +470,35 @@ void whenIdentifierPrefixIsUsed_thenAssignedClientIdStartsWithPrefix(final @NotN .awaitLog("received CONNACK"); } - @ParameterizedTest + @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"5"}) - void whenUserPropertyIsUsed_thenConnectSentWithUserProperties(final @NotNull String mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", - "-h", - hivemq.getHost(), - "-p", - String.valueOf(hivemq.getMqttPort()), - "-i", - "cliTest", - "-V", - mqttVersion, - "-up", - "key1=value1", - "-up", - "key2=value2"); + void whenUserPropertyIsUsed_thenConnectSentWithUserProperties() throws Exception { + final List connectCommand = defaultConnectCommand(); + connectCommand.add("-up"); + connectCommand.add("key1=value1"); + connectCommand.add("-up"); + connectCommand.add("key2=value2"); mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") .awaitLog("userProperties=[(key1, value1), (key2, value2)]") .awaitLog("received CONNACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + final ImmutableList userProperties = ImmutableList.builder() + .add(new MqttUserProperty("key1", "value1")) + .add(new MqttUserProperty("key2", "value2")) + .build(); + + connectAssertion.setUserProperties(UserPropertiesImpl.of(userProperties)); + }); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"3", "5"}) - void whenKeepAliveIsUsed_thenConnectContainsKeepAlive(final @NotNull String mqttVersion) throws Exception { + @ValueSource(chars = {'3', '5'}) + void whenKeepAliveIsUsed_thenConnectContainsKeepAlive(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", @@ -229,45 +508,48 @@ void whenKeepAliveIsUsed_thenConnectContainsKeepAlive(final @NotNull String mqtt "-i", "cliTest", "-V", - mqttVersion, + String.valueOf(mqttVersion), "-k", - "60"); + "30"); mqttCliShell.executeAsync(connectCommand) .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") - .awaitLog("keepAlive=60") + .awaitLog("keepAlive=30") .awaitLog("received CONNACK"); - } - - @ParameterizedTest - @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(strings = {"3", "5"}) - void test_rcvMax(final @NotNull String mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", - "-h", - hivemq.getHost(), - "-p", - String.valueOf(hivemq.getMqttPort()), - "-i", - "cliTest", - "-V", - mqttVersion, - "--rcvMax", - "60"); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setKeepAlive(30); + }); + } - if (mqttVersion.equals("3")) { - awaitOutput.awaitStdErr("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); - } else { - awaitOutput.awaitLog("receiveMaximum=60"); + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } - awaitOutput.awaitLog("received CONNACK"); + private @NotNull List defaultConnectCommand() { + final ArrayList defaultConnectCommand = new ArrayList<>(); + defaultConnectCommand.add("con"); + defaultConnectCommand.add("-h"); + defaultConnectCommand.add(hivemq.getHost()); + defaultConnectCommand.add("-p"); + defaultConnectCommand.add(String.valueOf(hivemq.getMqttPort())); + defaultConnectCommand.add("-i"); + defaultConnectCommand.add("cliTest"); + return defaultConnectCommand; + } + private @NotNull List defaultConnectCommand(final char mqttVersion) { + final List defaultConnectCommand = defaultConnectCommand(); + defaultConnectCommand.add("-V"); + defaultConnectCommand.add(String.valueOf(mqttVersion)); + return defaultConnectCommand; } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java new file mode 100644 index 000000000..abb9a5b68 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java @@ -0,0 +1,148 @@ +package com.hivemq.cli.utils; + +import com.google.common.collect.ImmutableList; +import com.hivemq.extension.sdk.api.annotations.Nullable; +import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.packets.connect.WillPublishPacket; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.UserProperties; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; +import org.jetbrains.annotations.NotNull; + +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.*; + +public class ConnectAssertion { + + private @NotNull MqttVersion mqttVersion = MqttVersion.V_5; + private @NotNull String clientId = "cliTest"; + private boolean cleanStart = true; + private long sessionExpiryInterval = 0; + private int keepAlive = 60; + private int receiveMaximum = 65535; + private long maximumPacketSize = 268435460; + private int topicAliasMaximum = 0; + private boolean requestProblemInformation = true; + private boolean requestResponseInformation = false; + + private @NotNull Optional userName = Optional.empty(); + private @NotNull Optional password = Optional.empty(); + private @NotNull Optional authenticationMethod = Optional.empty(); + private @NotNull Optional authenticationData = Optional.empty(); + + private @Nullable WillPublishPacket willPublish = null; + private @Nullable UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.builder().build()); + + private ConnectAssertion() { + + } + + public static void assertConnectPacket(final @NotNull ConnectPacket connectPacket, final @NotNull Consumer connectAssertionConsumer) { + final ConnectAssertion connectAssertion = new ConnectAssertion(); + connectAssertionConsumer.accept(connectAssertion); + assertEquals(connectAssertion.mqttVersion, connectPacket.getMqttVersion()); + assertEquals(connectAssertion.clientId, connectPacket.getClientId()); + assertEquals(connectAssertion.cleanStart, connectPacket.getCleanStart()); + assertEquals(connectAssertion.sessionExpiryInterval, connectPacket.getSessionExpiryInterval()); + assertEquals(connectAssertion.keepAlive, connectPacket.getKeepAlive()); + assertEquals(connectAssertion.receiveMaximum, connectPacket.getReceiveMaximum()); + assertEquals(connectAssertion.maximumPacketSize, connectPacket.getMaximumPacketSize()); + assertEquals(connectAssertion.topicAliasMaximum, connectPacket.getTopicAliasMaximum()); + assertEquals(connectAssertion.requestProblemInformation, connectPacket.getRequestProblemInformation()); + assertEquals(connectAssertion.requestResponseInformation, connectPacket.getRequestResponseInformation()); + + assertEquals(connectAssertion.userName, connectPacket.getUserName()); + assertEquals(connectAssertion.password, connectPacket.getPassword()); + assertEquals(connectAssertion.authenticationMethod, connectPacket.getAuthenticationMethod()); + assertEquals(connectAssertion.authenticationData, connectPacket.getAuthenticationData()); + + assertEquals(connectAssertion.userProperties, connectPacket.getUserProperties()); + if (connectAssertion.willPublish != null) { + assertTrue(connectPacket.getWillPublish().isPresent()); + final WillPublishPacket expectedWill = connectAssertion.willPublish; + final WillPublishPacket actualWill = connectPacket.getWillPublish().get(); + assertEquals(expectedWill.getPayload(), actualWill.getPayload()); + assertEquals(expectedWill.getTopic(), actualWill.getTopic()); + assertEquals(expectedWill.getWillDelay(), actualWill.getWillDelay()); + assertEquals(expectedWill.getUserProperties(), actualWill.getUserProperties()); + assertEquals(expectedWill.getContentType(), actualWill.getContentType()); + assertEquals(expectedWill.getCorrelationData(), actualWill.getCorrelationData()); + assertEquals(expectedWill.getMessageExpiryInterval(), actualWill.getMessageExpiryInterval()); + assertEquals(expectedWill.getQos(), actualWill.getQos()); + assertEquals(expectedWill.getResponseTopic(), actualWill.getResponseTopic()); + assertEquals(expectedWill.getRetain(), actualWill.getRetain()); + assertEquals(expectedWill.getSubscriptionIdentifiers(), actualWill.getSubscriptionIdentifiers()); + } else { + assertFalse(connectPacket.getWillPublish().isPresent()); + } + } + + public void setMqttVersion(final @NotNull MqttVersion mqttVersion) { + this.mqttVersion = mqttVersion; + } + + public void setClientId(final @NotNull String clientId) { + this.clientId = clientId; + } + + public void setCleanStart(final boolean cleanStart) { + this.cleanStart = cleanStart; + } + + public void setSessionExpiryInterval(final long sessionExpiryInterval) { + this.sessionExpiryInterval = sessionExpiryInterval; + } + + public void setKeepAlive(final int keepAlive) { + this.keepAlive = keepAlive; + } + + public void setReceiveMaximum(final int receiveMaximum) { + this.receiveMaximum = receiveMaximum; + } + + public void setMaximumPacketSize(final long maximumPacketSize) { + this.maximumPacketSize = maximumPacketSize; + } + + public void setTopicAliasMaximum(final int topicAliasMaximum) { + this.topicAliasMaximum = topicAliasMaximum; + } + + public void setRequestProblemInformation(final boolean requestProblemInformation) { + this.requestProblemInformation = requestProblemInformation; + } + + public void setRequestResponseInformation(final boolean requestResponseInformation) { + this.requestResponseInformation = requestResponseInformation; + } + + public void setUserName(final @Nullable String userName) { + this.userName = Optional.of(userName); + } + + public void setPassword(final @Nullable ByteBuffer password) { + this.password = Optional.of(password); + } + + public void setAuthenticationMethod(final @Nullable String authenticationMethod) { + this.authenticationMethod = Optional.of(authenticationMethod); + } + + public void setAuthenticationData(final @Nullable ByteBuffer authenticationData) { + this.authenticationData = Optional.of(authenticationData); + } + + public void setWillPublish(final @Nullable WillPublishPacket willPublish) { + this.willPublish = willPublish; + } + + public void setUserProperties(final @Nullable UserProperties userProperties) { + this.userProperties = userProperties; + } + +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java new file mode 100644 index 000000000..0681b502c --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -0,0 +1,123 @@ +package com.hivemq.cli.utils; + +import com.hivemq.embedded.EmbeddedExtension; +import com.hivemq.embedded.EmbeddedHiveMQ; +import com.hivemq.embedded.EmbeddedHiveMQBuilder; +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.annotations.NotNull; +import com.hivemq.extension.sdk.api.interceptor.connect.ConnectInboundInterceptor; +import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCallback { + + private EmbeddedHiveMQ hivemq; + private List connectPackets; + private int port; + + @Override + public void beforeAll(final ExtensionContext context) throws IOException { + + port = generatePort(); + + final String hivemqConfig = + "\n" + " " + + " \n" + " " + + " \n" + + " " + port + "\n" + + " 0.0.0.0\n" + + " \n" + + " \n" + + ""; + + final Path hivemqConfigFolder = Files.createTempDirectory("hivemq-config-folder"); + final File configXml = new File(hivemqConfigFolder.toAbsolutePath().toString(), "config.xml"); + assertTrue(configXml.createNewFile()); + Files.writeString(configXml.toPath(), hivemqConfig); + + final Path hivemqDataFolder = Files.createTempDirectory("hivemq-data-folder"); + + connectPackets = new ArrayList<>(); + final EmbeddedExtension embeddedExtension = EmbeddedExtension.builder() + .withId("test-interceptor-extension") + .withName("HiveMQ Test Interceptor Extension") + .withVersion("1.0.0") + .withPriority(0) + .withStartPriority(1000) + .withAuthor("HiveMQ") + .withExtensionMain(new ExtensionMain() { + @Override + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput) { + final ConnectInboundInterceptor connectInboundInterceptor = (connectInboundInput, connectInboundOutput) -> connectPackets.add(connectInboundInput.getConnectPacket()); + Services.interceptorRegistry().setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); + } + + @Override + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput) { + } + }).build(); + + final EmbeddedHiveMQBuilder builder = EmbeddedHiveMQ.builder() + .withConfigurationFolder(hivemqConfigFolder) + .withDataFolder(hivemqDataFolder) + .withEmbeddedExtension(embeddedExtension); + + try { + hivemq = builder.build(); + hivemq.start().join(); + } catch (final Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public void afterAll(final ExtensionContext context) { + hivemq.stop(); + } + + @Override + public void afterEach(final ExtensionContext context) { + connectPackets.clear(); + } + + public final @NotNull List getConnectPackets() { + return connectPackets; + } + + public int getMqttPort() { + return port; + } + + public @NotNull String getHost() { + return "localhost"; + } + + private int generatePort() throws IOException { + try (final ServerSocket socket = new ServerSocket(0);) { + return socket.getLocalPort(); + } + } +} + diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index 943ff658f..3a3f14024 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -39,7 +39,7 @@ public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { private ProcessIO processIO; private Process process; private LogWaiter logWaiter; - private Process orphanProcessDestroyer; + private Process orphanCleanupProcess; @Override public void beforeEach(final @NotNull ExtensionContext context) throws Exception { @@ -50,7 +50,7 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception // Start and await the start of the shell this.process = startShellMode(homeDir); - this.orphanProcessDestroyer = startOrphanProcessDestroyer(process); + this.orphanCleanupProcess = startOrphanCleanupProcess(process); this.processIO = ProcessIO.startReading(process); new AwaitOutput(processIO, null, String.join(" ", getShellCommand(homeDir))).awaitStdOut("mqtt>"); @@ -58,29 +58,29 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception this.logWaiter = setupLogWaiter(homeDir); } - private @NotNull Process startOrphanProcessDestroyer(final @NotNull Process childProcess) throws IOException { + @Override + public void afterEach(final @NotNull ExtensionContext context) { + process.destroyForcibly(); + orphanCleanupProcess.destroyForcibly(); + } + + private @NotNull Process startOrphanCleanupProcess(final @NotNull Process childProcess) throws IOException { // We start the ProcessGarbageCollector which sole job is to destroy the childProcess, meaning the mqtt-cli shell, // when the jvm process exited final long jvmProcessId = ProcessHandle.current().pid(); - final List processKillerCommand = List.of( + final List orphanCleanupProcessCommand = List.of( System.getProperty("java"), - Resources.getResource("OrphanProcessDestroyer.java").getPath(), + Resources.getResource("OrphanCleanupProcess.java").getPath(), String.valueOf(jvmProcessId), String.valueOf(childProcess.pid()) ); - final Process processKillerProcess = new ProcessBuilder(processKillerCommand).start(); + final Process orphanCleanupProcess = new ProcessBuilder(orphanCleanupProcessCommand).start(); // Wait until the process prints X, which means that the process garbage collector has registered the - final int readChar = processKillerProcess.getInputStream().read(); + final int readChar = orphanCleanupProcess.getInputStream().read(); assertEquals('X', readChar); - return processKillerProcess; - } - - @Override - public void afterEach(final @NotNull ExtensionContext context) { - process.destroyForcibly(); - orphanProcessDestroyer.destroyForcibly(); + return orphanCleanupProcess; } private @NotNull Process startShellMode(final @NotNull Path homeDir) throws IOException { diff --git a/src/systemTest/resources/OrphanProcessDestroyer.java b/src/systemTest/resources/OrphanCleanupProcess.java similarity index 59% rename from src/systemTest/resources/OrphanProcessDestroyer.java rename to src/systemTest/resources/OrphanCleanupProcess.java index b67e6a6eb..0282d4d27 100644 --- a/src/systemTest/resources/OrphanProcessDestroyer.java +++ b/src/systemTest/resources/OrphanCleanupProcess.java @@ -4,22 +4,19 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; -public class ProcessKiller { +public class OrphanCleanupProcess { - public static void main(final String[] args) - throws ExecutionException, InterruptedException, TimeoutException { + public static void main(final String[] args) throws ExecutionException, InterruptedException, TimeoutException { final String jvmProcessId = args[0]; final String childProcessId = args[1]; final ProcessHandle jvmProcess = ProcessHandle.of(Long.parseLong(jvmProcessId)).get(); final ProcessHandle childProcess = ProcessHandle.of(Long.parseLong(childProcessId)).get(); - final CompletableFuture future = - jvmProcess.onExit().whenComplete((processHandle, throwable) -> { - childProcess.destroyForcibly(); - System.exit(0); - }); + final CompletableFuture future = jvmProcess.onExit().whenComplete((processHandle, throwable) -> { + childProcess.destroyForcibly(); + }); System.out.println('X'); future.get(300, TimeUnit.SECONDS); } } - From 111a0756211a98e82a01fdc60ca3566c8cb6f716 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Sat, 8 Oct 2022 15:02:00 +0200 Subject: [PATCH 30/64] System Test > M1 compatibility --- .../hivemq/cli/utils/ConnectAssertion.java | 15 +++++++++++++ .../java/com/hivemq/cli/utils/HiveMQ.java | 21 ++++++++++++++++++- .../resources/OrphanCleanupProcess.java | 15 +++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java index abb9a5b68..82dcd3d83 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; import com.google.common.collect.ImmutableList; diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index 0681b502c..7c53cb508 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -1,5 +1,21 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; +import com.hivemq.configuration.service.InternalConfigurations; import com.hivemq.embedded.EmbeddedExtension; import com.hivemq.embedded.EmbeddedHiveMQ; import com.hivemq.embedded.EmbeddedHiveMQBuilder; @@ -12,6 +28,7 @@ import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.migration.meta.PersistenceType; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; @@ -86,6 +103,8 @@ public void extensionStop( try { hivemq = builder.build(); + InternalConfigurations.PAYLOAD_PERSISTENCE_TYPE.set(PersistenceType.FILE); + InternalConfigurations.RETAINED_MESSAGE_PERSISTENCE_TYPE.set(PersistenceType.FILE); hivemq.start().join(); } catch (final Exception ex) { ex.printStackTrace(); @@ -111,7 +130,7 @@ public int getMqttPort() { } public @NotNull String getHost() { - return "localhost"; + return "127.0.0.1"; } private int generatePort() throws IOException { diff --git a/src/systemTest/resources/OrphanCleanupProcess.java b/src/systemTest/resources/OrphanCleanupProcess.java index 0282d4d27..f8ac0191f 100644 --- a/src/systemTest/resources/OrphanCleanupProcess.java +++ b/src/systemTest/resources/OrphanCleanupProcess.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; import java.util.concurrent.CompletableFuture; From 0521dec7e2e62d9544748f26e6cd3a9dfe0f7874 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Sat, 8 Oct 2022 19:16:27 +0200 Subject: [PATCH 31/64] System Test > Add password:file test --- .../commands/cli/shell/ShellConnectST.java | 65 ++++++++++++++----- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java index d4bcd93c4..665237019 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java @@ -38,6 +38,8 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -56,7 +58,7 @@ public class ShellConnectST { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void whenHelpOptionIsUsed_thenUsageHelpIsDisplayed() throws Exception { + void test_help() throws Exception { final List connectCommand = List.of("con", "--help"); mqttCliShell.executeAsync(connectCommand).awaitStdOut("Usage").awaitStdOut("Options").awaitStdOut("mqtt>"); } @@ -64,7 +66,7 @@ void whenHelpOptionIsUsed_thenUsageHelpIsDisplayed() throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenConnect_thenConnectIsSuccess(final char mqttVersion) throws Exception { + void test_defaultConnect(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); mqttCliShell.executeAsync(connectCommand) @@ -79,7 +81,7 @@ void whenConnect_thenConnectIsSuccess(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenWrongPortIsUsed_thenConnectIsFailure(final char mqttVersion) throws Exception { + void test_wrongPort(final char mqttVersion) throws Exception { final List connectCommand = List.of("con", "-h", hivemq.getHost(), "-p", "22", "-V", String.valueOf(mqttVersion), "-i", "cliTest"); @@ -94,7 +96,7 @@ void whenWrongPortIsUsed_thenConnectIsFailure(final char mqttVersion) throws Exc @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenWrongHostIsUsed_thenConnectIsFailure(final char mqttVersion) throws Exception { + void test_wrongHost(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", @@ -117,7 +119,7 @@ void whenWrongHostIsUsed_thenConnectIsFailure(final char mqttVersion) throws Exc @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenNoCleanStartIsUsed_thenConnectCleanStartIsFalse(final char mqttVersion) throws Exception { + void test_cleanStart(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--no-cleanStart"); @@ -135,7 +137,7 @@ void whenNoCleanStartIsUsed_thenConnectCleanStartIsFalse(final char mqttVersion) @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenReceiveMaximumIsUsed_thenConnectContainsReceiveMaximum(final char mqttVersion) throws Exception { + void test_receiveMaximum(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--rcvMax"); connectCommand.add("500"); @@ -160,7 +162,7 @@ void whenReceiveMaximumIsUsed_thenConnectContainsReceiveMaximum(final char mqttV @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenMaxPacketSizeIsUsed_thenConnectPacketContainsMaxPacketSize(final char mqttVersion) throws Exception { + void test_maxPacketSize(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--maxPacketSize"); connectCommand.add("500"); @@ -185,7 +187,7 @@ void whenMaxPacketSizeIsUsed_thenConnectPacketContainsMaxPacketSize(final char m @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenTopicAliasMaxIsUsed_thenConnectContainsTopicAliasMax(final char mqttVersion) throws Exception { + void test_topicAliasMaximum(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--topicAliasMax"); connectCommand.add("5"); @@ -210,7 +212,7 @@ void whenTopicAliasMaxIsUsed_thenConnectContainsTopicAliasMax(final char mqttVer @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenRequestProblemInformationIsUSed_thenConnectContainsRequestProblemInformation(final char mqttVersion) + void test_requestProblemInformation(final char mqttVersion) throws IOException { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--no-reqProblemInfo"); @@ -236,7 +238,7 @@ void whenRequestProblemInformationIsUSed_thenConnectContainsRequestProblemInform @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenRequestResponseInformationIsUsed_thenConnectContainsRequestResponseInformation(final char mqttVersion) + void test_requestResponseInformation(final char mqttVersion) throws IOException { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--reqResponseInfo"); @@ -262,7 +264,7 @@ void whenRequestResponseInformationIsUsed_thenConnectContainsRequestResponseInfo @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenNoClientIdIsUsed_thenConnectIsSuccess(final char mqttVersion) throws Exception { + void test_noClientId(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", @@ -288,7 +290,7 @@ void whenNoClientIdIsUsed_thenConnectIsSuccess(final char mqttVersion) throws Ex @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenUserNameIsUsed_thenConnectPacketContainsUserName(final char mqttVersion) throws Exception { + void test_userName(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("-u"); connectCommand.add("username"); @@ -307,7 +309,7 @@ void whenUserNameIsUsed_thenConnectPacketContainsUserName(final char mqttVersion @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenPasswordIsUsed_thenConnectPacketContainsPassword(final char mqttVersion) throws Exception { + void test_password(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("-pw"); connectCommand.add("password"); @@ -330,7 +332,34 @@ void whenPasswordIsUsed_thenConnectPacketContainsPassword(final char mqttVersion @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenWillIsUsed_thenConnectContainsWill(final char mqttVersion) throws Exception { + void test_passwordFile(final char mqttVersion) throws Exception { + + final Path passwordFile = Files.createTempFile("mqtt-cli-password", ".txt"); + Files.writeString(passwordFile, "password", StandardCharsets.UTF_8); + + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-pw:file"); + connectCommand.add(passwordFile.toString()); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + } else { + awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_will(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("-Wt"); connectCommand.add("test-will-topic"); @@ -405,7 +434,7 @@ void whenWillIsUsed_thenConnectContainsWill(final char mqttVersion) throws Excep @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void whenSessionExpiryIsUsed_thenSessionIsUsed() throws Exception { + void test_sessionExpiryInterval() throws Exception { final List connectCommand = List.of( "con", "-h", @@ -451,7 +480,7 @@ void whenSessionExpiryIsUsed_thenSessionIsUsed() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void whenIdentifierPrefixIsUsed_thenAssignedClientIdStartsWithPrefix() throws Exception { + void test_identifierPrefix() throws Exception { final List connectCommand = List.of( "con", "-h", @@ -472,7 +501,7 @@ void whenIdentifierPrefixIsUsed_thenAssignedClientIdStartsWithPrefix() throws Ex @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void whenUserPropertyIsUsed_thenConnectSentWithUserProperties() throws Exception { + void test_userProperties() throws Exception { final List connectCommand = defaultConnectCommand(); connectCommand.add("-up"); connectCommand.add("key1=value1"); @@ -498,7 +527,7 @@ void whenUserPropertyIsUsed_thenConnectSentWithUserProperties() throws Exception @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void whenKeepAliveIsUsed_thenConnectContainsKeepAlive(final char mqttVersion) throws Exception { + void test_keepAlive(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", From 31378d44b6bf08cbdf4b93a24f92e2220161c2ee Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 10:03:54 +0200 Subject: [PATCH 32/64] System Tests > Add password tests --- .../commands/cli/shell/ShellConnectEnvST.java | 109 ++++++++++++++++++ .../commands/cli/shell/ShellConnectST.java | 54 ++++++++- .../com/hivemq/cli/utils/MqttCliShell.java | 15 ++- 3 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java new file mode 100644 index 000000000..78ede4da4 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java @@ -0,0 +1,109 @@ +package com.hivemq.cli.commands.cli.shell; + +import com.hivemq.cli.utils.AwaitOutput; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static org.junit.jupiter.api.Assertions.fail; + +public class ShellConnectEnvST { + + private static String PASSWORD_ENV = "PASSWORD"; + + @RegisterExtension + static HiveMQ hivemq = new HiveMQ(); + + @RegisterExtension + final MqttCliShell mqttCliShell = new MqttCliShell(Map.of(PASSWORD_ENV, "password")); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_passwordEnv(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-pw:env"); + connectCommand.add(PASSWORD_ENV); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + awaitOutput.awaitStdOut("mqtt>"); + } else { + awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userNameAndPasswordEnv(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-u"); + connectCommand.add("user"); + connectCommand.add("-pw:env"); + connectCommand.add(PASSWORD_ENV); + + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); + + awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("user"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } + + private @NotNull List defaultConnectCommand() { + final ArrayList defaultConnectCommand = new ArrayList<>(); + defaultConnectCommand.add("con"); + defaultConnectCommand.add("-h"); + defaultConnectCommand.add(hivemq.getHost()); + defaultConnectCommand.add("-p"); + defaultConnectCommand.add(String.valueOf(hivemq.getMqttPort())); + defaultConnectCommand.add("-i"); + defaultConnectCommand.add("cliTest"); + return defaultConnectCommand; + } + + private @NotNull List defaultConnectCommand(final char mqttVersion) { + final List defaultConnectCommand = defaultConnectCommand(); + defaultConnectCommand.add("-V"); + defaultConnectCommand.add(String.valueOf(mqttVersion)); + return defaultConnectCommand; + } +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java index 665237019..60bfdf6c4 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java @@ -212,8 +212,7 @@ void test_topicAliasMaximum(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_requestProblemInformation(final char mqttVersion) - throws IOException { + void test_requestProblemInformation(final char mqttVersion) throws IOException { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--no-reqProblemInfo"); @@ -238,8 +237,7 @@ void test_requestProblemInformation(final char mqttVersion) @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_requestResponseInformation(final char mqttVersion) - throws IOException { + void test_requestResponseInformation(final char mqttVersion) throws IOException { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--reqResponseInfo"); @@ -318,6 +316,7 @@ void test_password(final char mqttVersion) throws Exception { if (mqttVersion == '3') { awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + awaitOutput.awaitStdOut("mqtt>"); } else { awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") @@ -329,6 +328,27 @@ void test_password(final char mqttVersion) throws Exception { } } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userNameAndPassword(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-u"); + connectCommand.add("user"); + connectCommand.add("-pw"); + connectCommand.add("password"); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("user"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -345,6 +365,7 @@ void test_passwordFile(final char mqttVersion) throws Exception { if (mqttVersion == '3') { awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + awaitOutput.awaitStdOut("mqtt>"); } else { awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") @@ -356,6 +377,31 @@ void test_passwordFile(final char mqttVersion) throws Exception { } } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userNameAndPasswordFile(final char mqttVersion) throws Exception { + + final Path passwordFile = Files.createTempFile("mqtt-cli-password", ".txt"); + Files.writeString(passwordFile, "password", StandardCharsets.UTF_8); + + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("-u"); + connectCommand.add("user"); + connectCommand.add("-pw:file"); + connectCommand.add(passwordFile.toString()); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("user"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index 3a3f14024..3c3818911 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -29,6 +29,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -41,6 +42,16 @@ public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { private LogWaiter logWaiter; private Process orphanCleanupProcess; + private @NotNull Map envVariables; + + public MqttCliShell() { + envVariables = Map.of(); + } + + public MqttCliShell(final @NotNull Map envVariables) { + this.envVariables = envVariables; + } + @Override public void beforeEach(final @NotNull ExtensionContext context) throws Exception { @@ -85,7 +96,9 @@ public void afterEach(final @NotNull ExtensionContext context) { private @NotNull Process startShellMode(final @NotNull Path homeDir) throws IOException { final List shellCommand = getShellCommand(homeDir); - return new ProcessBuilder(shellCommand).start(); + final ProcessBuilder processBuilder = new ProcessBuilder(shellCommand); + processBuilder.environment().putAll(envVariables); + return processBuilder.start(); } private @NotNull List getShellCommand(final Path homeDir) { From 04df47422340acd792cb773d1ec0dc2e32d84200 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 10:04:07 +0200 Subject: [PATCH 33/64] System Tests > Add license --- .../cli/commands/cli/shell/ShellConnectEnvST.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java index 78ede4da4..8fd73bf8a 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.shell; import com.hivemq.cli.utils.AwaitOutput; From ae1af0ca5ffb750bcb55c4df9db36c973e727500 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 10:32:50 +0200 Subject: [PATCH 34/64] System Tests > Add MQTT 3 version to tests --- .../commands/cli/shell/ShellConnectEnvST.java | 4 +- .../commands/cli/shell/ShellConnectST.java | 210 +++++++++++------- .../hivemq/cli/utils/ConnectAssertion.java | 8 +- 3 files changed, 139 insertions(+), 83 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java index 8fd73bf8a..dc00fec6c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java @@ -20,7 +20,6 @@ import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -38,7 +37,7 @@ public class ShellConnectEnvST { - private static String PASSWORD_ENV = "PASSWORD"; + private static final String PASSWORD_ENV = "PASSWORD"; @RegisterExtension static HiveMQ hivemq = new HiveMQ(); @@ -59,6 +58,7 @@ void test_passwordEnv(final char mqttVersion) throws Exception { if (mqttVersion == '3') { awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); awaitOutput.awaitStdOut("mqtt>"); + awaitOutput.awaitLog("Password-Only Authentication is not allowed in MQTT 3"); } else { awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) .awaitLog("sending CONNECT") diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java index 60bfdf6c4..0ef0534a7 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java @@ -97,8 +97,7 @@ void test_wrongPort(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_wrongHost(final char mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", "unreachable-host", "-p", @@ -142,15 +141,17 @@ void test_receiveMaximum(final char mqttVersion) throws Exception { connectCommand.add("--rcvMax"); connectCommand.add("500"); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("received CONNACK"); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); } + awaitOutput.awaitLog("sending CONNECT"); + awaitOutput.awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); if (mqttVersion == '5') { @@ -167,15 +168,17 @@ void test_maxPacketSize(final char mqttVersion) throws Exception { connectCommand.add("--maxPacketSize"); connectCommand.add("500"); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("received CONNACK"); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1"); } + awaitOutput.awaitLog("sending CONNECT"); + awaitOutput.awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); if (mqttVersion == '5') { @@ -192,15 +195,17 @@ void test_topicAliasMaximum(final char mqttVersion) throws Exception { connectCommand.add("--topicAliasMax"); connectCommand.add("5"); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("received CONNACK"); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1"); } + awaitOutput.awaitLog("sending CONNECT"); + awaitOutput.awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); if (mqttVersion == '5') { @@ -216,16 +221,19 @@ void test_requestProblemInformation(final char mqttVersion) throws IOException { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--no-reqProblemInfo"); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("received CONNACK"); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr( "Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog( + "Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1"); } + awaitOutput.awaitLog("sending CONNECT"); + awaitOutput.awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); if (mqttVersion == '5') { @@ -241,16 +249,19 @@ void test_requestResponseInformation(final char mqttVersion) throws IOException final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--reqResponseInfo"); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("received CONNACK"); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr( "Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog( + "Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1"); } + awaitOutput.awaitLog("sending CONNECT"); + awaitOutput.awaitLog("received CONNACK"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); if (mqttVersion == '5') { @@ -263,8 +274,7 @@ void test_requestResponseInformation(final char mqttVersion) throws IOException @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_noClientId(final char mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", hivemq.getHost(), "-p", @@ -316,6 +326,7 @@ void test_password(final char mqttVersion) throws Exception { if (mqttVersion == '3') { awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + awaitOutput.awaitLog("Password-Only Authentication is not allowed in MQTT 3"); awaitOutput.awaitStdOut("mqtt>"); } else { awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) @@ -365,6 +376,7 @@ void test_passwordFile(final char mqttVersion) throws Exception { if (mqttVersion == '3') { awaitOutput.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + awaitOutput.awaitLog("Password-Only Authentication is not allowed in MQTT 3"); awaitOutput.awaitStdOut("mqtt>"); } else { awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) @@ -439,6 +451,13 @@ void test_will(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Will Response Topic was set but is unused in MQTT Version MQTT_3_1_1"); awaitOutput.awaitLog("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitStdErr("Will Message Expiry was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitStdErr("Will Payload Format was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitStdErr("Will Delay Interval was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitStdErr("Will Content Type was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitStdErr("Will Response Topic was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitStdErr("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1"); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { final WillPublishBuilder expectedWillBuilder = Builders.willPublish() .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) @@ -478,104 +497,137 @@ void test_will(final char mqttVersion) throws Exception { } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_sessionExpiryInterval() throws Exception { - final List connectCommand = List.of( - "con", + @ValueSource(chars = {'3', '5'}) + void test_sessionExpiryInterval(final char mqttVersion) throws Exception { + final List connectCommand = List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), + "-V", + String.valueOf(mqttVersion), "-se", "120", "-i", "sessionTest", "--no-cleanStart"); - mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("sessionExpiryInterval=120") - .awaitLog("received CONNACK") - .awaitLog("sessionPresent=false"); + if (mqttVersion == '3') { + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitStdErr("Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1") + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); - final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); - assertConnectPacket(connectPacket1, connectAssertion -> { - connectAssertion.setClientId("sessionTest"); - connectAssertion.setCleanStart(false); - connectAssertion.setSessionExpiryInterval(120); - }); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId("sessionTest"); + }); - mqttCliShell.executeAsync(List.of("dis")).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); + } else { + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("sessionExpiryInterval=120") + .awaitLog("received CONNACK") + .awaitLog("sessionPresent=false"); + + final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); + assertConnectPacket(connectPacket1, connectAssertion -> { + connectAssertion.setClientId("sessionTest"); + connectAssertion.setCleanStart(false); + connectAssertion.setSessionExpiryInterval(120); + }); - mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("sessionExpiryInterval=120") - .awaitLog("received CONNACK") - .awaitLog("sessionPresent=true"); + mqttCliShell.executeAsync(List.of("dis")).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); - final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); - assertConnectPacket(connectPacket2, connectAssertion -> { - connectAssertion.setClientId("sessionTest"); - connectAssertion.setCleanStart(false); - connectAssertion.setSessionExpiryInterval(120); - }); + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("sessionExpiryInterval=120") + .awaitLog("received CONNACK") + .awaitLog("sessionPresent=true"); + + final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); + assertConnectPacket(connectPacket2, connectAssertion -> { + connectAssertion.setClientId("sessionTest"); + connectAssertion.setCleanStart(false); + connectAssertion.setSessionExpiryInterval(120); + }); + } } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_identifierPrefix() throws Exception { - final List connectCommand = List.of( - "con", + @ValueSource(chars = {'3', '5'}) + void test_identifierPrefix(final char mqttVersion) throws Exception { + final List connectCommand = List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-V", - "3", + String.valueOf(mqttVersion), "-ip", "myPrefix"); - mqttCliShell.executeAsync(connectCommand) - .awaitStdOut("myPrefix") - .awaitStdOut(String.format("@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("received CONNACK"); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdOut("myPrefix"); + } + + awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending CONNECT"); + awaitOutput.awaitLog("received CONNACK"); + } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_userProperties() throws Exception { - final List connectCommand = defaultConnectCommand(); + @ValueSource(chars = {'3', '5'}) + void test_userProperties(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("-up"); connectCommand.add("key1=value1"); connectCommand.add("-up"); connectCommand.add("key2=value2"); - mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) - .awaitLog("sending CONNECT") - .awaitLog("userProperties=[(key1, value1), (key2, value2)]") - .awaitLog("received CONNACK"); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - final ImmutableList userProperties = ImmutableList.builder() - .add(new MqttUserProperty("key1", "value1")) - .add(new MqttUserProperty("key2", "value2")) - .build(); + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1"); + } + + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending CONNECT"); + + if (mqttVersion == '5') { + awaitOutput.awaitLog("userProperties=[(key1, value1), (key2, value2)]"); + } + + awaitOutput.awaitLog("received CONNACK"); - connectAssertion.setUserProperties(UserPropertiesImpl.of(userProperties)); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + final ImmutableList userProperties = ImmutableList.builder() + .add(new MqttUserProperty("key1", "value1")) + .add(new MqttUserProperty("key2", "value2")) + .build(); + connectAssertion.setUserProperties(UserPropertiesImpl.of(userProperties)); + } }); + } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_keepAlive(final char mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", hivemq.getHost(), "-p", diff --git a/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java index 82dcd3d83..492f539e9 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils; import com.google.common.collect.ImmutableList; @@ -50,13 +51,16 @@ public class ConnectAssertion { private @NotNull Optional authenticationData = Optional.empty(); private @Nullable WillPublishPacket willPublish = null; - private @Nullable UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.builder().build()); + private @Nullable UserProperties userProperties = + UserPropertiesImpl.of(ImmutableList.builder().build()); private ConnectAssertion() { } - public static void assertConnectPacket(final @NotNull ConnectPacket connectPacket, final @NotNull Consumer connectAssertionConsumer) { + public static void assertConnectPacket( + final @NotNull ConnectPacket connectPacket, + final @NotNull Consumer connectAssertionConsumer) { final ConnectAssertion connectAssertion = new ConnectAssertion(); connectAssertionConsumer.accept(connectAssertion); assertEquals(connectAssertion.mqttVersion, connectPacket.getMqttVersion()); From 5fa50f5344b257ce057b8e7b531903725ccae04a Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 15:00:17 +0200 Subject: [PATCH 35/64] System Tests > Add tls and websockets tests --- .../{ => connect}/ShellConnectEnvST.java | 4 +- .../shell/{ => connect}/ShellConnectST.java | 47 ++++++- .../cli/shell/connect/ShellConnectTlsST.java | 88 +++++++++++++ .../connect/ShellConnectWebsocketsST.java | 75 +++++++++++ .../java/com/hivemq/cli/utils/HiveMQ.java | 121 +++++++++++++++++- .../resources/tls/broker-keystore.jks | Bin 0 -> 2415 bytes src/systemTest/resources/tls/certly.sh | 72 +++++++++++ src/systemTest/resources/tls/client-cert.crt | Bin 0 -> 674 bytes src/systemTest/resources/tls/client-cert.pem | 17 +++ src/systemTest/resources/tls/client-key.pem | 30 +++++ .../resources/tls/client-keystore.jks | Bin 0 -> 2021 bytes .../resources/tls/client-keystore.p12 | Bin 0 -> 2997 bytes src/systemTest/resources/tls/server.crt | Bin 0 -> 717 bytes src/systemTest/resources/tls/server.pem | 17 +++ 14 files changed, 462 insertions(+), 9 deletions(-) rename src/systemTest/java/com/hivemq/cli/commands/cli/shell/{ => connect}/ShellConnectEnvST.java (97%) rename src/systemTest/java/com/hivemq/cli/commands/cli/shell/{ => connect}/ShellConnectST.java (94%) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java create mode 100644 src/systemTest/resources/tls/broker-keystore.jks create mode 100755 src/systemTest/resources/tls/certly.sh create mode 100644 src/systemTest/resources/tls/client-cert.crt create mode 100644 src/systemTest/resources/tls/client-cert.pem create mode 100644 src/systemTest/resources/tls/client-key.pem create mode 100644 src/systemTest/resources/tls/client-keystore.jks create mode 100644 src/systemTest/resources/tls/client-keystore.p12 create mode 100644 src/systemTest/resources/tls/server.crt create mode 100644 src/systemTest/resources/tls/server.pem diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java similarity index 97% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java index dc00fec6c..0a161a90c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.cli.shell.connect; import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; @@ -40,7 +40,7 @@ public class ShellConnectEnvST { private static final String PASSWORD_ENV = "PASSWORD"; @RegisterExtension - static HiveMQ hivemq = new HiveMQ(); + static HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension final MqttCliShell mqttCliShell = new MqttCliShell(Map.of(PASSWORD_ENV, "password")); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java similarity index 94% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java index 0ef0534a7..f2684c239 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.cli.shell.connect; import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.AwaitOutput; @@ -51,7 +51,7 @@ public class ShellConnectST { @RegisterExtension - static HiveMQ hivemq = new HiveMQ(); + static HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension final MqttCliShell mqttCliShell = new MqttCliShell(); @@ -78,6 +78,49 @@ void test_defaultConnect(final char mqttVersion) throws Exception { assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(toVersion(mqttVersion))); } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectWhileConnected(final char mqttVersion) throws Exception { + final List connectCommand1 = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-V", String.valueOf(mqttVersion), + "-i", "client1" + ); + + final List connectCommand2 = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-V", String.valueOf(mqttVersion), + "-i", "client2" + ); + + mqttCliShell.executeAsync(connectCommand1) + .awaitStdOut(String.format("client1@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); + assertConnectPacket(connectPacket1, connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId("client1"); + }); + + mqttCliShell.executeAsync(connectCommand2) + .awaitStdOut(String.format("client2@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); + assertConnectPacket(connectPacket2, connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId("client2"); + }); + } + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java new file mode 100644 index 000000000..5d5e97787 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.shell.connect; + +import com.google.common.io.Resources; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static org.junit.jupiter.api.Assertions.fail; + +public class ShellConnectTlsST { + + @RegisterExtension + static HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); + + @RegisterExtension + final MqttCliShell mqttCliShell = new MqttCliShell(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_mutualTls(final char mqttVersion) throws Exception { + + final String clientKeyPem = Resources.getResource("tls/client-key.pem").getPath(); + final String clientCertPem = Resources.getResource("tls/client-cert.pem").getPath(); + final String serverPem = Resources.getResource("tls/server.pem").getPath(); + + final List connectCommand = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttTlsPort()), + "-V", String.valueOf(mqttVersion), + "-i", "cliTest", + "--cafile", + serverPem, + "--key", + clientKeyPem, + "--cert", + clientCertPem + ); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut("Enter private key password:"); + + mqttCliShell.executeAsync(List.of("changeme")) + .awaitStdOut(String.format("cliTest@%s", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java new file mode 100644 index 000000000..a114cf984 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.shell.connect; + +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static org.junit.jupiter.api.Assertions.fail; + +public class ShellConnectWebsocketsST { + + @RegisterExtension + static HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); + + @RegisterExtension + final MqttCliShell mqttCliShell = new MqttCliShell(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_defaultConnect(final char mqttVersion) throws Exception { + final List connectCommand = List.of( + "con", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getWebsocketPort()), + "-V", String.valueOf(mqttVersion), + "-i", "cliTest", + "-ws", + "-ws:path", + hivemq.getWebsocketsPath() + ); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(toVersion(mqttVersion))); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index 7c53cb508..e4680b57b 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -15,6 +15,7 @@ */ package com.hivemq.cli.utils; +import com.google.common.io.Resources; import com.hivemq.configuration.service.InternalConfigurations; import com.hivemq.embedded.EmbeddedExtension; import com.hivemq.embedded.EmbeddedHiveMQ; @@ -46,22 +47,45 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCallback { + private static final String WEBSOCKETS_PATH = "/mqtt-custom"; + private static final String BIND_ADDRESS = "localhost"; + private EmbeddedHiveMQ hivemq; private List connectPackets; - private int port; + + private int port = -1; + private int tlsPort = -1; + private int websocketPort = -1; + + private final boolean tlsEnabled; + private final boolean websocketEnabled; + + public static @NotNull Builder builder() { + return new Builder(); + } + + public HiveMQ(final boolean tlsEnabled, final boolean websocketEnabled) { + this.tlsEnabled = tlsEnabled; + this.websocketEnabled = websocketEnabled; + } @Override public void beforeAll(final ExtensionContext context) throws IOException { - port = generatePort(); + this.port = generatePort(); + + final String tlsConfig = setupTls(); + final String websocketsConfig = setupWebsockets(); final String hivemqConfig = "\n" + " " + " \n" + " " + " \n" + " " + port + "\n" + - " 0.0.0.0\n" + + " " + BIND_ADDRESS + "\n" + " \n" + + tlsConfig + + websocketsConfig + " \n" + ""; @@ -129,14 +153,101 @@ public int getMqttPort() { return port; } + public int getMqttTlsPort() { + if (tlsPort == -1) { + throw new RuntimeException("HiveMQ was initialized without a TLS listener."); + } + return tlsPort; + } + + public int getWebsocketPort() { + if (websocketPort == -1) { + throw new RuntimeException("HiveMQ was initialized without a websocket listener."); + } + return websocketPort; + } + + public @NotNull String getWebsocketsPath() { + return WEBSOCKETS_PATH; + } + public @NotNull String getHost() { - return "127.0.0.1"; + return BIND_ADDRESS; } private int generatePort() throws IOException { - try (final ServerSocket socket = new ServerSocket(0);) { + try (final ServerSocket socket = new ServerSocket(0)) { return socket.getLocalPort(); } } + + private @NotNull String setupTls() throws IOException { + String tlsConfig = ""; + if (tlsEnabled) { + this.tlsPort = generatePort(); + final String brokerKeyStorePath = Resources.getResource("tls/broker-keystore.jks").getPath(); + final String brokerTrustStorePath = Resources.getResource("tls/client-keystore.jks").getPath(); + tlsConfig = + "\n" + + " " + tlsPort + "\n" + + " " + BIND_ADDRESS + "\n" + + " \n" + + " " + + " " + brokerKeyStorePath + "\n" + + " changeme\n" + + " changeme\n" + + " \n" + + " REQUIRED\n" + + " \n" + + " " + brokerTrustStorePath + "\n" + + " changeme\n" + + " \n" + + " \n" + + "\n"; + } + return tlsConfig; + } + + private @NotNull String setupWebsockets() throws IOException { + String websocketsConfig = ""; + if (websocketEnabled) { + this.websocketPort = generatePort(); + websocketsConfig = + "\n" + + " " + websocketPort + "\n" + + " " + BIND_ADDRESS + "\n" + + " " + WEBSOCKETS_PATH + "\n" + + " my-websocket-listener\n" + + " \n" + + " mqttv3.1\n" + + " mqtt\n" + + " \n" + + " true\n" + + ""; + } + return websocketsConfig; + } + + public static class Builder { + private boolean tlsEnabled = false; + private boolean websocketEnabled = false; + + private Builder() { + } + + public @NotNull HiveMQ build() { + return new HiveMQ(tlsEnabled, websocketEnabled); + } + + public @NotNull Builder withTlsEnabled(final boolean tlsEnabled) { + this.tlsEnabled = tlsEnabled; + return this; + } + + public @NotNull Builder withWebsocketEnabled(final boolean websocketEnabled) { + this.websocketEnabled = websocketEnabled; + return this; + } + } } diff --git a/src/systemTest/resources/tls/broker-keystore.jks b/src/systemTest/resources/tls/broker-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..2264ee4cfbcb9f687191e26b54fc98f6c2ac5e8b GIT binary patch literal 2415 zcmY+Ec{~%0AICT5n7LZagvuSE&ACU}sK=RRXl{xSq2<1>WsWkrCvq*hjUFD!T`E_O zkZU-&1WKcDaS&o_by_Td0>B8Xr)Zm@EqN#c$GkOx>o z1dD=*;3L1V0D{Qn{jZ420Yv1o`Gu{14VD}7zbk$|AV&!iw1*&qb`VP3eE-M4o=bp4 z-uletOp-K2%@!UCJ?~5qKgxwN=HLYM;6OxBA3y$TSQzT_{9?+A8b;?s{)`oKBQ0b0 zltzhOf=y(v_})nJv*L5sZ7HTHRro)SOuUvKbzZm&<5h76?Xm(!&Oh-fc4yHLosl7h zoSAv=_sO6m*LOjJ-%rQFSSXfJ`%PHXrMl1O3O&GST06|^=(l_>!h6A8tw7{Pgp~cG zZ%fjZMxr%tPnv)`1@BaDP-C+aP5GVC!co?H#-55|20F$9Uo8W5TByQ|tWGt-f+HBe zQ~kf|SOnKC?mtfvm8Fg+b=qhJ1%h{)*1(*LNnFgaa^=|y?OJ1(wcIKG~#DGvATl^{-P$Yma6n9E0$J} z&(q|S+#gr+1F3~(y1FdYDPNiDHIaJz>hL@X*GbyEv6=AW{Qd_lVN>nvW3!w;s_w6t z_BI|^_X}@GexeuxDkTJW7FTP(LJ7_AoN0;3-2>UtD@Qp5;!yJEaaWS2^x=-~d9(#5 zs93Mt5xJ<=C&gLq7R`zzDW9O)L6qLUF_WKR|{ozEpI2S;As%o z#%JwJ2e3mjnNMt^WudJg$*<`{0~|LzW8xoFMcNI0b|xgbo=2{ty_omA-9v}Qjo}|b zW38LV8@-X15J=O!Ym~#@FmdABS=&tZ<=qA3+;&^m%g@+`+b81-Iv$2!if3*gHy>mj z1U%C_hm42Vb0 z=m%^x?^L&N!!ytkMKLsB9Vagxp7J*HL;0Xa{vEK){fDxeL; zYP}Dh9Pd$@(GsH)d=BdjZWHVPjlUNuCf?-(ohuf9sT-O$WG;oFlocDD3YO`|T$cie z=fkMOclc=OxY1Op#UrZMtL_DpFLwku@E5ZcEU?iPYx&V!z4pEh1VTMj0Up^BQw_%- zs$~ZRyZ3m0e6b;Wl6lQ4Cp6qTJ}o8GLfsZ-to2R#)fYn*dS_|XPnfPlKd3R2#Z(@QKMXZro{}ufL1=qcvu~VAEtNB^h{IUj8B&t75y>;pjXrAA zvOg4fVVaRU@VZ5gveZwc9@>e>VBIm_-`zT3(oUStbgj1!I5FGtrEfmk9LJzWtn`|y zCO7slXNTx1U2Z`Qs_?g~I2`Vn#76O%GaNqsN^Mk5mdAXL1MRjlg)VeRZhm$@@L#jzE~JW=2Od z^)}Q8sgIEOTa{oDq~LLWfEU0U5CZT3_yPP8(*J~_ND&b9vbUedNu;*6mL>wJp{f3x zwhn^G`QYyo4|fTXll%*%Z~y_n7V)11_%Fk9|HrUvk7z<+ayPWnN#YANt71c~T?uLb zHEbM_)5JW8eqZH9Y_;9xv{q*cPVd^inCT3s`IgD%UAiQt$Mf}{gw(gUWEz`EZE?Kr zB99`MmM_W7;tjGhAgqv-8Nc|7__`Sfo>|Xe>{n&f&+&u(CPHWJcgvr&X_WAK!fWg?bSp-u#eVC@{`;y9B@g8M65t(tc@0mpz+gK#+L+ zN}X4(zBCfw=f6yH>j)lSfv!mzds-h8+A`5&2xa&GSjy!C_}Mz{t$Fi6 z*2QIKk`C154K!m`|E1OApoqs~UImYJf%Bgm)_VftrE z_no<-A7~oTMPbnmE8lGBs5pvucB1DVy%qA}=4unmlM5w!(tEPejQOzGevEz3f_@zb zptLB=t4#QMba}N8mXJ=|vb+T;St;(eo7O~Pt08k%EP7h&tvp}OGinj2bD^bUr3NPZ z(kB{i)<4tTNa>ShVD5bUjG+)f^mx>d5!3Or-9vSs3eGNbvBORm#p!cH}D4@QsBhl}V zWlR}t3l*EMq3O+0I&j#Sj3${a6&`NP=DR5{^|k^f)4Rt|q>oyVOe(4e#iD%9j4}>h zLpgcTJV1iLt5yUWA&mfYbDb9C/dev/null; + +#restore original directory +popd > /dev/null +#where am i? +echo "Certificates saved in the output directory: $(pwd)/$outDirName" diff --git a/src/systemTest/resources/tls/client-cert.crt b/src/systemTest/resources/tls/client-cert.crt new file mode 100644 index 0000000000000000000000000000000000000000..cd4b0cb8633357a8cb76730c66757a3d8f3936ee GIT binary patch literal 674 zcmXqLVwz{r#Ms8f$#7SPXE}S9&07OrHcqWJkGAi;jEvl@3 znzh-EfHJE^i_MlEtF7C3?n_1Hfu|2sjaYa0mL01~ZdX{_V&Q5is9CmGcsH}^ySEC= z!Rr@q+MIIzu3ywmW8;O3itR+ETCEm*uux{({>kRrI^T_URCL?@{WYiTR6R%8EtYBZ zXZTHLbWa@V3sG!s8z+Zt%QH`?{K` zL3n-cL4KX0YA+RTTOzsQr^r@GiD|2u3KR{OE~!#2NU0pZ~Jb3 z&vxrF(%-5^8Z;b+V^U=|0-EL)!%SSSAuMio_{)Lo{0EMdySAl{o2DCt+Eo8i(_@Gw!Cgooq6s-MNQ{+o1pr` zqGAgg6E~~#PnPJN(e1zQf9mtE->N&+nU|{a1nQrd#(%}wq-f>8rnZ|+je?H9pDjMO zC{<{2*wdv^y6Nc^i*AP|dVQ6szdUhLh=6!{SHvGlc7?>9s#lvPUzYs*Lhd5Nr|L}+ m`?}A}d;Q^MOeXBJ`V@u27oiRzC+DqxS4x6)goK{zg&kJ8*J` z_m8J8JOOS8qhTBZ_>)Uhb9yKB09w9b^@p)<-nuVu?q2CV^Tt$V)&a>+`=3~SeUWMp z-RWWqgoAs@B|!`OYpRJJIV4ISqlNg2nkiU=`Q0oA3Opi3nrUk>8pTDlmY{&?Gfm=8 zYcZTXh0ofbVUz)(NF5diYgF94af!o<}G|Zw0l`%b&)%d zGbv8Ok`WoNALQUUC3<^&rvb?AeqaHr&f%an!K42un}jJPwgoT0(k4Jeg%Lsju}LJK zDWlZqdwtcH0r(1Ry=i{v1=otc{T5#sCKOfEB{EGXv9gz64PbR$!~bC@O7PF#+&${i6J@~xxXNK z9HTdza*(WfmGZSOY#d58@=?I3KnJ4uPcw!Ci_ZM%)0qe+r+ZZd%->N<-jf-|F5djo z$vQBHYBkd>cjXeD5rK@5Mb0YN0{qd=&-r}bi?2S58>vBrWoyZ(SU^}w{AEJj$*2Kp za_u^Khy-=`ai@s;k$hLkj&JX%VKStij-T;a^l;%{DM5U}6-g@z&cc%`N79BIDAFJ1 z&a_mwE`+wK5n)};Hi&%d{HNM;azPrFG@j3>zWI5iE@1qB7xNn7;}hBB`y4mEi+Jt# zqh9GSyMWKtoIM2P_kXvyYD&IIo;^SDew%M?7H;S+jMny`=c1RFWD;V`SLHPI(^++r ze?M#T+8w0v@*(y89n&_d4*kbp?31U1Cu8a;*3HtdYhN;p>P91~#=rPQGKqms7|L&{ zGJvZ)%W>Gfo9XhN;G`FO0ui1B(a&Pi`A6l)&Vf}C%X5u$%RZ2nzj^9_8GA2gp%$PG zKmtnEP#qhCH5G4G!KmDxE(Hc69CzUaxbV(dIX6Vq!aI zoFH*?uS|-jG3T<0d76LM&p4uGJ5ewDti5H2x`f}nAIXu+#@|p5IdKl>Kazf{6A4x; zR*{Q0DYzaKUkSshS#635PZr6YUe#gN=dz(GqCcequNrYAa;1@o(K=_#NC^N%W39=F z`;W;~TrKrTg#^*GRYO{`wxbqwDvjirN$y4#_V4uy0M7q5q<{EcrL(D;(1P0DKxVP= zwMbiZ_@Qonv1-5SH=VFv)rN9t&9hqavCG6Lqf6eaR?ly1Wk*%I{MITwX}cdw^(O!z zvuV$K_j3^5J7>#G+*mD;p{*|ql3r|A4lNo!?nhe+b`-3Q#xOC4z@w0K%$yRp0BRT1 zi**agKt;H%u`6$GQ9vEiB^)8HO!lOr0^4-6^649r)T9#N@cjo(Ti;M)a9CTZ%NC;x z8IvR$S+(~Nu~XC09TrttfP;Ym000311z0XMFgXAK0-`X20-i8}0fqtz0NpGMrw58W z?Jx}n2`Yw2hW8Bt0Sg5HFcC2iFbxI+RRjYJ24ie#Wo~pZ9v2NVGBGeQFflPQIWRL? z7Y#BqF)%PSF)=bZFf&>(5it)i4F&^M1Op5PV{B<&LNQU6&A1-p%Q#(HCiAgzQr zMKKX6cD)n3110Y5AOlmcqq4JP*WFH9*)ubtqH{hJl{%{t;GrD0zmqpBEcY_Fc#A&$ z`kZ#ke+YKk1eSly4>g*JpId9+_tQ%cQgK6$RGC@mO+f_W0M-SwZudqr{88I1%yGx@ zc-Rc;X7#HAfD^B6!4E9gHc%#D>@BJMGeWyKNr#-#oiDO`vf$+>@g;Han8`6RglK_mZ~L@VBXxB7IWl}|CW zc5MAv{sdwqPh&fs`_ ziMKpbf5R0Ppn+ktCl8YtjhKs1zW-(C_4a#-Cj+G=3{fx0mJif3G;*o_f`-|Gfe}Ib z=A+J{WfG%S<)vCJXJ>e#+f`vo^%#HCk&;vp7iWrC{22!zVYwyMf|Ju3^XMMY0P=gX zSiXzQp6l?&O&2h-mbCUKo7qNUnxf9A$UzymFKu)C+5LvSZI|l`7AJy1)PPZ5^=RV~)g# zr6fa`yGZ1C`n_Jy^ZcIYkMBR9&+GO5^YcbxnCd~mGe`_mC>>NL!6;#$8AuN-!7$wd zW0*Yt#4bn-9nU`%T?vMc;ZFnx0Rc!1$(&Vfvw5;=`5uni8JeNq8Qs4$qL7@LXcKus=xildTx zW%}_PEz{x*ZoFIExYy9=Jh4?82K$8v7B$+D)R#@}1t`>CHVmnjz5c$XU$#Uuem*H7 z#{O4EhK?gM_B)fl>~>Il?%|4R!1SWkm#5!n7c&{GwC>d2-^WpsrWNsIu4znct8n-e zRZDuDuzN^UK2ThO!kow3?4Q>(+;Z{D+?E7E?-r5!k^n6v!>pp7e_;?VWP67{5C6i^ z*F|_2l_UYcDq*K9vMPEPHi6_t<8$}!VA&zDX%flcMo$ZM$e0&7ejF2*9}{TyQ&o;d z=0;K4eth|Yt@}cr)x?wa>GWyDh9U*_ga2T)q~$6&Ta+rcB>OZ^o5%sEzdZ9ZgOsek z8`t1*YSYV~wOi&38qNj!nZKqvNynx?I2o6-`yH%hbN&kP7}i!3N@w)4t3_5K9}a)- zkBppd^D`fwkssPT>IkICtg}Rc|=>%`RQZYbDtJ; zD(J76%>r{COEVaw_P+RjS4|CbD5LkI?Tt^$m0ntRM!bn|av;7GZM81osWHoy$T}X} zV7Fci6jv77F%Jt}8xW49@cHUDL0%Djt$zZVKje<|1w(t{BwJU85ngr?7A2?mPTHT3JOoGc+DX@LSh4nr z@jg_hqo-#0fii#L721%n`;n0x$ELsBqp{2!a}=e)`$4!#r@ro1(#l+1Z^mrMYLXP) ze8)0^zNf~49gmA>3AyPae4cY%C;L#4aE*PJ;#`jk)AdI^Se8R2Z*?cxTkM+^>MdN7 zhU)$JxElCzFi=*(rS{l{3cL4J=+`>p`=AWzMq;ET=X~a)IV1cK}(qfv3kqg-gRUz^c=aB3bcI(`|M=ts_drw|F0X!$I^UbiyiHOj%OT637o z{?<3dxV*s>TLnv`6gRMoo5nMZ+UzX}N)pQO6D~Q1yVul;GI-gcMoN0+H|6hb*y;&m z1gxH()nFnk)v5Ih@0{k9!~l$M8k?ojR&8Ih8Cnt7D6_Y@hKuB_n@;Y@Gnr)r3Pap2 zJm&5FzTS9Xl?7S6#hkU?pU>77#X@_QFr;JD*P4=0P#C;nz6~Mqm~7;*Lr(hBjN*6P zj&~qD&&X?44-|INu6XWxuC2@)l+8otjc3V;~5o7aRIK6 z-Rp5Pc2&ClDCZo!9>#%y$od^i_IcO(E1n=pJJjMFN!0VQB7mcUMx9sEOAG$22M4T&hl2E%r+|j|@Yo zdPK2?ENWd~8S34K3hZq_k>b_T-Cl5m*_xD#_xM<-ast6M)v=WbC`R9OH(kK91F4gj z_U+Ovk1ii_BvYcjoOuXlYYzw-R6sn6%heA67jz$i~?;67C#(Ly!@< z7_z@HL6y>Fc50nfjOvlAT-X+gu~2;VwVOfDtZ6@Sd)Dk0i1_ zw+C0}_h;@@l?q)UN1Ikw)4wkSImjf|#({<+@iqCZSA6-M z70yj>!T0BKL4Y!{;$>`@q%`8~quUM3 z*JmYuTyQx#@DEgR@M?DeoynG(7RHagBYr60<@z1U=E$@fd&Uj3E;{_w*?UU<*c#;F?llxvn) zaDjSJ`+38wb7=gGNM8cRn42z4zJqmBq(jh##J3&&yUtxjBch8Qz(Dx4-utQ>)*#!T z4$2bv9Th{B9gwgo4wVBPmByd6H%vOs8DERsWtM$W5WLKIEz^b~@)+8dt^FXu)Ip%D zeZxN-_}SaOS=@VbMe`At&zSX>QY^%WH++cJ7<`)iJkyrv`qB62rW9+f+R<{im*wEa zuAe4OE_5&lv&;OiW15a{Mk`B-+L8G}jRR9B4%E)2P(Ei(C$wJGq79Cl1h;7xYnSPe z#@A^kB5KkBCRY7e2YQ#FhfQVEdil3K;y5t$`%Rq00jQJOj=A0 zPtkr78xA1=L_u2k6Bci0^3t9M+|%tRk+Etun6W5$qtK=)56#;c3)AnCm_<)r^~+eG zYq2!AqmCrkjty>2LW53B1J=~IACj4?28~$KEcsmDaAxMnHTF0VVvs`UHx*BCn@#9V z*StB#_&Rpm!V~veAMU=vppk-{2vr`DvybYc4(-p5Cb}mBPCN|kY_7q*z-r)>fz|bh zUCh>*^p74N86;dff~b}U&$K?cf?7$GnujfLxKs01`sU{~V(hT!qr_0%-vC+2lO6O< zG8?=nF5nvrzoz(}t{pVw(1>#;!`NRe!;WDr+|46Qo5D{h%DFiMBxrX*x-~MKh6AVP zC6G_I^nls`OQz-lea@Qk6Hsvx_BWN<{N}*S=X?4c7a)++KM2FmKiv`&2LM= zl|U!-v{W(_&mzVDyN*yUMQ$)>nSiopv(EMj0*}&f_G!?%(?0-5Dk8;@P&$Y#%NY+>dL+~EPYJzN2JuSD;;rz8LkPD{FZERDSxlzzW__eiE{t| literal 0 HcmV?d00001 diff --git a/src/systemTest/resources/tls/server.crt b/src/systemTest/resources/tls/server.crt new file mode 100644 index 0000000000000000000000000000000000000000..75b5c631bf497569dc3d4323280ea0255c0668eb GIT binary patch literal 717 zcmXqLVmfKi#JF(*GZP~d6HCTfeg^|yHcqWJkGAi;jEvl@3yf}Loho+xb(Td1vx=$GndTrsjxUBDusMB<_nYqFt zKUOopZ~f6KysnGyW=00a#fk><2C~2~m*ry-V-Zo&5S{U0N~D;4+k`toH+=&4 z?koC=9EiYJ0tO-@gLk>c$$y62Ga0;Y$+C+aOn>)6<+#PMBj-zhem;7ZZ7%nO$Ij9F znoW&O>h7rRmg{)AFzTj`z`>{2ylW1Wc#8bL^mp-0wff&#Yj&SVi0Qk(%j@Mr{*$ZL zy89`4F5ePj$h74=`){E?cA1k5ww!!kCbZh=aJ0rU|8pXV``o7Xhs1QAE7)e_IrFV? z5bqPW^uqrBl-84?5*Jv_$|v}*;p>a_G2H#zq81;c`c8}Y|i+{Dd+6v95)TgO49l6;i&$ICUlCZz2pI0*| bxSbTell!TlF5aiO@_N(4+oDfG?p*``Qko>; literal 0 HcmV?d00001 diff --git a/src/systemTest/resources/tls/server.pem b/src/systemTest/resources/tls/server.pem new file mode 100644 index 000000000..49c2f738d --- /dev/null +++ b/src/systemTest/resources/tls/server.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICyTCCAbGgAwIBAgIEaM0PQDANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwls +b2NhbGhvc3QwIBcNMjIxMDEwMTEyOTAyWhgPMjEyMTA1MDQxMTI5MDJaMBQxEjAQ +BgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AL4SXZkX+pTbnOdUv+2w6ZgfnAHOZCWV1gAYmNyokxQ5LPcNoevyV0BmKVGc1T2b +ViYv8kF3/r22QZwReVpoLVdqhe3BBt4a0DWVenHjCR+AqnwIa7TmmNCxHb5csASL +0nRqZb6QHhCVJo8mRQnqSGOLiCUfMYQh1BpsiI9B5b8lGqy4B2izBDoP+iShGCUU +/23ioYeYNvt5hyOfm/Ui7tmlyzVVIvL5F7eH4u+cx5vOgJ5fJwXLaAjfyho09Cl6 +NjQwtqZhki2GBiOfK/k55082rykp0Zu6CClOSnKoWZYt5QDBSrQI0aaO7BVClzaZ +bRNU+KsD74X4hROuig7pm3UCAwEAAaMhMB8wHQYDVR0OBBYEFCQoFZjglFkWH4aQ +3FLZTFG9vnL1MA0GCSqGSIb3DQEBCwUAA4IBAQBLdyjJ/jELmQBK2h0HFMFn7ugk +xzjGxM91+fPFzQadC5DjQ1u+gzUzNH7cJrseiOmhWtksEMHl1kt8wHRJFP/S/aOZ +Jn/7aqy7yGBcjt+6SumhD8mqrUdOIkmntFQxArTPB/sS/D5pkjC0yed2EqtCw1so +pk/OFGG+RpWPVFyJznC2MkmZ7TNSDeRGZ3GPj2SFyRUY0AU2d5BPrA6OXUwxu/zl +LrMLTNgRUXiROGCSHva9IO+koESC2QKdFyuKHkWAiPlHpF8ZK8Fa8AqbjEbR0f0r +ioZxN5R/ZicCxNmGHwyh36RW/WXzqwAgRskV3G3ycH5fTHN514Kh2xXkVN7R +-----END CERTIFICATE----- From de2a5115dd6a654d9da87b4332d62921cf3c091a Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 17:05:07 +0200 Subject: [PATCH 36/64] README > Add ShellDisconnect tests --- .../hivemq/cli/mqtt/MqttClientExecutor.java | 2 +- .../hivemq/cli/commands/cli/PublishST.java | 16 +- .../hivemq/cli/commands/cli/SubscribeST.java | 16 +- .../commands/cli/shell/ShellDisconnectST.java | 161 ++++++++++++++++-- .../commands/cli/shell/ShellPublishST.java | 38 ++--- .../commands/cli/shell/ShellSubscribeST.java | 31 ++-- .../cli/shell/connect/ShellConnectEnvST.java | 2 +- .../cli/shell/connect/ShellConnectST.java | 2 +- .../cli/shell/connect/ShellConnectTlsST.java | 2 +- .../connect/ShellConnectWebsocketsST.java | 4 +- .../java/com/hivemq/cli/utils/HiveMQ.java | 35 +++- .../com/hivemq/cli/utils/MqttCliShell.java | 36 +++- .../{ => assertions}/ConnectAssertion.java | 3 +- .../utils/assertions/DisconnectAssertion.java | 69 ++++++++ .../assertions/DisconnectInformation.java | 22 +++ 15 files changed, 339 insertions(+), 100 deletions(-) rename src/systemTest/java/com/hivemq/cli/utils/{ => assertions}/ConnectAssertion.java (99%) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java diff --git a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java index de6d65be9..a99d739a0 100644 --- a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java +++ b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java @@ -324,7 +324,7 @@ void mqtt5Disconnect(final @NotNull Mqtt5Client client, final @NotNull Disconnec @Override void mqtt3Disconnect(final @NotNull Mqtt3Client client, final @NotNull Disconnect disconnect) { - Logger.debug("{} Sending DISCONNECT", LoggerUtils.getClientPrefix(client.getConfig())); + Logger.debug("{} sending DISCONNECT", LoggerUtils.getClientPrefix(client.getConfig())); client.toBlocking().disconnect(); } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index d3be4ad94..029f67af6 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -17,6 +17,7 @@ package com.hivemq.cli.commands.cli; import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; @@ -26,6 +27,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; import java.util.List; @@ -37,21 +39,11 @@ public class PublishST { - private static final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); + @RegisterExtension + private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); private final @NotNull MqttCli mqttCli = new MqttCli(); - @BeforeAll - static void beforeAll() { - hivemq.start(); - } - - @AfterAll - static void afterAll() { - hivemq.stop(); - } - @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_publish() throws Exception { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 4f281264c..688633749 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -18,6 +18,7 @@ import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; @@ -27,6 +28,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.utility.DockerImageName; import java.nio.charset.StandardCharsets; @@ -38,21 +40,11 @@ public class SubscribeST { - private static final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq-ce")); + @RegisterExtension + private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); private final @NotNull MqttCli mqttCli = new MqttCli(); - @BeforeAll - static void beforeAll() { - hivemq.start(); - } - - @AfterAll - static void afterAll() { - hivemq.stop(); - } - @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_successful_subscribe() throws Exception { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java index 4815e9d04..abe25a4ea 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java @@ -16,7 +16,12 @@ package com.hivemq.cli.commands.cli.shell; +import com.google.common.collect.ImmutableList; +import com.hivemq.cli.utils.AwaitOutput; +import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -24,40 +29,168 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.utility.DockerImageName; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import static com.hivemq.cli.utils.assertions.DisconnectAssertion.assertDisconnectPacket; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class ShellDisconnectST { - private static final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); + @RegisterExtension + static @NotNull HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); - @BeforeAll - static void beforeAll() { - hivemq.start(); + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_successfulDisconnect(final char mqttVersion) throws Exception { + final List disconnectCommand = List.of("dis"); + mqttCliShell.connectClient(hivemq, mqttVersion, "myClient"); + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); + assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + disconnectAssertion.setDisconnectedClient("myClient"); + }); } - @AfterAll - static void afterAll() { - hivemq.stop(); + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_sessionExpiryInterval(final char mqttVersion) throws Exception { + final List connectCommand = List.of( + "con", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "cliTest", + "-se", + "120"); + final List disconnectCommand = List.of("dis", "-e", "60"); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); + assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + if (mqttVersion == '5') { + disconnectAssertion.setSessionExpiryInterval(60); + } + }); } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_disconnect() throws Exception { - final List disconnectCommand = List.of("dis"); - mqttCliShell.connectClient(hivemq); - mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>"); + @ValueSource(chars = {'3', '5'}) + void test_reasonString(final char mqttVersion) throws Exception { + final List disconnectCommand = List.of("dis", "-r", "test-reason"); + + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Reason string was set but is unused in Mqtt version MQTT_3_1_1"); + } + + assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + if (mqttVersion == '5') { + disconnectAssertion.setReasonString("test-reason"); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userProperties(final char mqttVersion) throws Exception { + final List disconnectCommand = List.of("dis", "-up", "key1=value1", "-up", "key2=value2"); + + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("User properties were set but are unused in Mqtt version MQTT_3_1_1"); + } + + assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + if (mqttVersion == '5') { + final UserPropertiesImpl expectedUserProperties = + UserPropertiesImpl.of(ImmutableList.builder() + .add(MqttUserProperty.of("key1", "value1")) + .add(MqttUserProperty.of("key2", "value2")) + .build()); + disconnectAssertion.setUserProperties(expectedUserProperties); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_disconnectById(final char mqttVersion) throws Exception { + final String clientId = "myTestClient"; + final List disconnectCommand = List.of("dis", "-i", clientId); + + mqttCliShell.connectClient(hivemq, mqttVersion, clientId); + mqttCliShell.executeAsync(List.of("exit")).awaitStdOut("mqtt>"); + mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); + + assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + disconnectAssertion.setDisconnectedClient(clientId); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_disconnectAll(final char mqttVersion) throws Exception { + final List disconnectAllCommand = List.of("dis", "-a"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + + mqttCliShell.executeAsync(disconnectAllCommand) + .awaitStdOut("mqtt>") + .awaitLog("sending DISCONNECT") + .awaitLog("sending DISCONNECT") + .awaitLog("sending DISCONNECT"); + + final String clientId1 = hivemq.getDisconnectInformations().get(0).getClientId(); + final String clientId2 = hivemq.getDisconnectInformations().get(1).getClientId(); + final String clientId3 = hivemq.getDisconnectInformations().get(2).getClientId(); + final ArrayList clientIdPool = new ArrayList<>(); + clientIdPool.add(clientId1); + clientIdPool.add(clientId2); + clientIdPool.add(clientId3); + + assertTrue(clientIdPool.containsAll(List.of("client1", "client2", "client3"))); + + assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + disconnectAssertion.setDisconnectedClient(clientId1); + }); + + assertDisconnectPacket(hivemq.getDisconnectInformations().get(1), disconnectAssertion -> { + disconnectAssertion.setDisconnectedClient(clientId2); + }); + + assertDisconnectPacket(hivemq.getDisconnectInformations().get(2), disconnectAssertion -> { + disconnectAssertion.setDisconnectedClient(clientId3); + }); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_unsuccessful_disconnect() throws Exception { + void test_notConnectedDisconnect() throws Exception { final List disconnectCommand = List.of("dis"); mqttCliShell.executeAsync(disconnectCommand) .awaitStdErr("Missing required option '--identifier='") diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java index 43ac1e006..23fe1ed4c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java @@ -16,6 +16,7 @@ package com.hivemq.cli.commands.cli.shell; +import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; @@ -24,6 +25,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.utility.DockerImageName; import java.util.List; @@ -31,45 +34,38 @@ public class ShellPublishST { - private static final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); + @RegisterExtension + private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); - @BeforeAll - static void beforeAll() { - hivemq.start(); - } - - @AfterAll - static void afterAll() { - hivemq.stop(); - } - - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_publish() throws Exception { + @ValueSource(chars = {'3', '5'}) + void test_successful_publish(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test"); - mqttCliShell.connectClient(hivemq); + mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(publishCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_topic() throws Exception { + @ValueSource(chars = {'3', '5'}) + void test_publish_missing_topic(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub"); - mqttCliShell.connectClient(hivemq); + mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Missing required option: '--topic '") .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_message() throws Exception { + @ValueSource(chars = {'3', '5'}) + void test_publish_missing_message(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test"); - mqttCliShell.connectClient(hivemq); + mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Error: Missing required argument (specify one of these)") .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java index aeeabdce1..13df862ab 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java @@ -16,6 +16,7 @@ package com.hivemq.cli.commands.cli.shell; +import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; @@ -24,6 +25,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.utility.DockerImageName; import java.util.List; @@ -31,35 +34,27 @@ public class ShellSubscribeST { - private static final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); + @RegisterExtension + private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); - @BeforeAll - static void beforeAll() { - hivemq.start(); - } - - @AfterAll - static void afterAll() { - hivemq.stop(); - } - - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_subscribe() throws Exception { + @ValueSource(chars = {'3', '5'}) + void test_successful_subscribe(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test"); - mqttCliShell.connectClient(hivemq); + mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(subscribeCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_subscribe_missing_topic() throws Exception{ + @ValueSource(chars = {'3', '5'}) + void test_subscribe_missing_topic(final char mqttVersion) throws Exception{ final List subscribeCommand = List.of("sub"); - mqttCliShell.connectClient(hivemq); + mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) .awaitStdErr("Missing required option: '--topic '") .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java index 0a161a90c..8a39277c5 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java @@ -32,7 +32,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.fail; public class ShellConnectEnvST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java index f2684c239..4241f8cfe 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java @@ -44,7 +44,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java index 5d5e97787..491072b91 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.fail; public class ShellConnectTlsST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java index a114cf984..181213862 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.fail; public class ShellConnectWebsocketsST { @@ -46,7 +46,7 @@ void test_defaultConnect(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getWebsocketPort()), + "-p", String.valueOf(hivemq.getWebsocketsPort()), "-V", String.valueOf(mqttVersion), "-i", "cliTest", "-ws", diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index e4680b57b..09c50618c 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -16,19 +16,27 @@ package com.hivemq.cli.utils; import com.google.common.io.Resources; +import com.hivemq.cli.utils.assertions.DisconnectInformation; import com.hivemq.configuration.service.InternalConfigurations; import com.hivemq.embedded.EmbeddedExtension; import com.hivemq.embedded.EmbeddedHiveMQ; import com.hivemq.embedded.EmbeddedHiveMQBuilder; import com.hivemq.extension.sdk.api.ExtensionMain; import com.hivemq.extension.sdk.api.annotations.NotNull; +import com.hivemq.extension.sdk.api.client.ClientContext; +import com.hivemq.extension.sdk.api.client.parameter.InitializerInput; import com.hivemq.extension.sdk.api.interceptor.connect.ConnectInboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.disconnect.DisconnectInboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.disconnect.parameter.DisconnectInboundInput; +import com.hivemq.extension.sdk.api.interceptor.disconnect.parameter.DisconnectInboundOutput; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; import com.hivemq.migration.meta.PersistenceType; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; @@ -51,11 +59,13 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCal private static final String BIND_ADDRESS = "localhost"; private EmbeddedHiveMQ hivemq; + private List connectPackets; + private List disconnectInformations; private int port = -1; private int tlsPort = -1; - private int websocketPort = -1; + private int websocketsPort = -1; private final boolean tlsEnabled; private final boolean websocketEnabled; @@ -73,6 +83,8 @@ public HiveMQ(final boolean tlsEnabled, final boolean websocketEnabled) { public void beforeAll(final ExtensionContext context) throws IOException { this.port = generatePort(); + this.connectPackets = new ArrayList<>(); + this.disconnectInformations = new ArrayList<>(); final String tlsConfig = setupTls(); final String websocketsConfig = setupWebsockets(); @@ -96,7 +108,6 @@ public void beforeAll(final ExtensionContext context) throws IOException { final Path hivemqDataFolder = Files.createTempDirectory("hivemq-data-folder"); - connectPackets = new ArrayList<>(); final EmbeddedExtension embeddedExtension = EmbeddedExtension.builder() .withId("test-interceptor-extension") .withName("HiveMQ Test Interceptor Extension") @@ -111,6 +122,9 @@ public void extensionStart( final @NotNull ExtensionStartOutput extensionStartOutput) { final ConnectInboundInterceptor connectInboundInterceptor = (connectInboundInput, connectInboundOutput) -> connectPackets.add(connectInboundInput.getConnectPacket()); Services.interceptorRegistry().setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); + Services.initializerRegistry().setClientInitializer((initializerInput, clientContext) -> clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> { + disconnectInformations.add(new DisconnectInformation(disconnectInboundInput.getDisconnectPacket(), disconnectInboundInput.getClientInformation().getClientId())); + })); } @Override @@ -143,12 +157,17 @@ public void afterAll(final ExtensionContext context) { @Override public void afterEach(final ExtensionContext context) { connectPackets.clear(); + disconnectInformations.clear(); } - public final @NotNull List getConnectPackets() { + public @NotNull List getConnectPackets() { return connectPackets; } + public @NotNull List getDisconnectInformations() { + return disconnectInformations; + } + public int getMqttPort() { return port; } @@ -160,11 +179,11 @@ public int getMqttTlsPort() { return tlsPort; } - public int getWebsocketPort() { - if (websocketPort == -1) { + public int getWebsocketsPort() { + if (websocketsPort == -1) { throw new RuntimeException("HiveMQ was initialized without a websocket listener."); } - return websocketPort; + return websocketsPort; } public @NotNull String getWebsocketsPath() { @@ -211,10 +230,10 @@ private int generatePort() throws IOException { private @NotNull String setupWebsockets() throws IOException { String websocketsConfig = ""; if (websocketEnabled) { - this.websocketPort = generatePort(); + this.websocketsPort = generatePort(); websocketsConfig = "\n" + - " " + websocketPort + "\n" + + " " + websocketsPort + "\n" + " " + BIND_ADDRESS + "\n" + " " + WEBSOCKETS_PATH + "\n" + " my-websocket-listener\n" + diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index 3c3818911..8a260bd9c 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -17,7 +17,7 @@ package com.hivemq.cli.utils; import com.google.common.io.Resources; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; @@ -31,9 +31,9 @@ import java.util.List; import java.util.Map; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { @@ -41,6 +41,7 @@ public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { private Process process; private LogWaiter logWaiter; private Process orphanCleanupProcess; + private int connectClientMarker = 0; private @NotNull Map envVariables; @@ -129,17 +130,28 @@ public void afterEach(final @NotNull ExtensionContext context) { /** * Connects a mqtt-client and awaits the successful output statements on std-out and in the logfile. * - * @param hivemq the HiveMQ container to which the client should connect + * @param hivemq the HiveMQ instance to which the client should connect * @throws IOException when the cli command to connect could not be written to the shell */ - public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) throws IOException { + public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion) throws IOException { + connectClient(hivemq, mqttVersion, "cliTest"); + } + + public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, final @NotNull String clientId) throws IOException { final List connectCommand = - List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-i", "cliTest"); + List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-V", String.valueOf(mqttVersion), "-i", clientId); final AwaitOutput awaitOutput = - executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + executeAsync(connectCommand).awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())); awaitOutput.awaitLog("received CONNACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(connectClientMarker), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId(clientId); + }); + + connectClientMarker+= 1; } /** @@ -156,4 +168,14 @@ public void connectClient(final @NotNull HiveMQTestContainerExtension hivemq) th return new AwaitOutput(processIO, logWaiter, fullCommand); } + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } + } diff --git a/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/ConnectAssertion.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java rename to src/systemTest/java/com/hivemq/cli/utils/assertions/ConnectAssertion.java index 492f539e9..5af1ecddf 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ConnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/ConnectAssertion.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.assertions; import com.google.common.collect.ImmutableList; import com.hivemq.extension.sdk.api.annotations.Nullable; @@ -55,7 +55,6 @@ public class ConnectAssertion { UserPropertiesImpl.of(ImmutableList.builder().build()); private ConnectAssertion() { - } public static void assertConnectPacket( diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java new file mode 100644 index 000000000..738018b9a --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java @@ -0,0 +1,69 @@ +package com.hivemq.cli.utils.assertions; + +import com.google.common.collect.ImmutableList; +import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; +import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectReasonCode; +import com.hivemq.extension.sdk.api.packets.general.UserProperties; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DisconnectAssertion { + + private @NotNull DisconnectReasonCode disconnectReasonCode = DisconnectReasonCode.NORMAL_DISCONNECTION; + private @NotNull Optional reasonString = Optional.empty(); + private @NotNull Optional serverReference = Optional.empty(); + private @NotNull UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.builder().build()); + private @NotNull Optional sessionExpiryInterval = Optional.empty(); + + private @Nullable String disconnectedClient = null; + + private DisconnectAssertion() { + } + + public static void assertDisconnectPacket(final @NotNull DisconnectInformation disconnectInformation, final @NotNull Consumer disconnectAssertionConsumer) { + final DisconnectAssertion disconnectAssertion = new DisconnectAssertion(); + disconnectAssertionConsumer.accept(disconnectAssertion); + + final DisconnectPacket disconnectPacket = disconnectInformation.getDisconnectPacket(); + assertEquals(disconnectAssertion.disconnectReasonCode, disconnectPacket.getReasonCode()); + assertEquals(disconnectAssertion.reasonString, disconnectPacket.getReasonString()); + assertEquals(disconnectAssertion.serverReference, disconnectPacket.getServerReference()); + assertEquals(disconnectAssertion.userProperties, disconnectPacket.getUserProperties()); + assertEquals(disconnectAssertion.sessionExpiryInterval, disconnectPacket.getSessionExpiryInterval()); + + if (disconnectAssertion.disconnectedClient != null) { + assertEquals(disconnectAssertion.disconnectedClient, disconnectInformation.getClientId()); + } + } + + public void setDisconnectReasonCode(final @NotNull DisconnectReasonCode disconnectReasonCode) { + this.disconnectReasonCode = disconnectReasonCode; + } + + public void setReasonString(final @NotNull String reasonString) { + this.reasonString = Optional.of(reasonString); + } + + public void setServerReference(final @NotNull String serverReference) { + this.serverReference = Optional.of(serverReference); + } + + public void setUserProperties(final @NotNull UserProperties userProperties) { + this.userProperties = userProperties; + } + + public void setSessionExpiryInterval(final long sessionExpiryInterval) { + this.sessionExpiryInterval = Optional.of(sessionExpiryInterval); + } + + public void setDisconnectedClient(final @NotNull String disconnectedClient) { + this.disconnectedClient = disconnectedClient; + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java new file mode 100644 index 000000000..ad80a77f4 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java @@ -0,0 +1,22 @@ +package com.hivemq.cli.utils.assertions; + +import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; +import org.jetbrains.annotations.NotNull; + +public class DisconnectInformation { + private final @NotNull DisconnectPacket disconnectPacket; + private final @NotNull String clientId; + + public DisconnectInformation(final @NotNull DisconnectPacket disconnectPacket, final @NotNull String clientId) { + this.disconnectPacket = disconnectPacket; + this.clientId = clientId; + } + + public @NotNull DisconnectPacket getDisconnectPacket() { + return disconnectPacket; + } + + public @NotNull String getClientId() { + return clientId; + } +} From e4a096fa107622d542cbcc1903b98d9268739379 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 17:35:00 +0200 Subject: [PATCH 37/64] System Tests > Add license headers --- .../cli/utils/assertions/DisconnectAssertion.java | 15 +++++++++++++++ .../utils/assertions/DisconnectInformation.java | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java index 738018b9a..7b0b6e224 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils.assertions; import com.google.common.collect.ImmutableList; diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java index ad80a77f4..e6352c40c 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils.assertions; import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; From fe8d1a82f936a7644ae140bfa05c812d4ac775b4 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 17:58:10 +0200 Subject: [PATCH 38/64] System Tests > Add publish assertion --- .../commands/cli/shell/ShellDisconnectST.java | 7 +- .../commands/cli/shell/ShellPublishST.java | 11 +- .../cli/shell/connect/ShellConnectEnvST.java | 2 +- .../cli/shell/connect/ShellConnectST.java | 2 +- .../cli/shell/connect/ShellConnectTlsST.java | 2 +- .../connect/ShellConnectWebsocketsST.java | 2 +- .../java/com/hivemq/cli/utils/HiveMQ.java | 21 +++- .../utils/assertions/PublishAssertion.java | 104 ++++++++++++++++++ 8 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/assertions/PublishAssertion.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java index abe25a4ea..7d8be1ee2 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java @@ -22,29 +22,24 @@ import com.hivemq.cli.utils.MqttCliShell; import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.testcontainers.utility.DockerImageName; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.assertions.DisconnectAssertion.assertDisconnectPacket; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class ShellDisconnectST { @RegisterExtension - static @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private final static @NotNull HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java index 23fe1ed4c..d70cc3fe8 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java @@ -29,13 +29,17 @@ import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.utility.DockerImageName; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; +import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; + public class ShellPublishST { @RegisterExtension - private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -47,6 +51,11 @@ void test_successful_publish(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test"); mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(publishCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + }); } @ParameterizedTest diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java index 8a39277c5..2de2c8bfe 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java @@ -40,7 +40,7 @@ public class ShellConnectEnvST { private static final String PASSWORD_ENV = "PASSWORD"; @RegisterExtension - static HiveMQ hivemq = HiveMQ.builder().build(); + private final static HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension final MqttCliShell mqttCliShell = new MqttCliShell(Map.of(PASSWORD_ENV, "password")); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java index 4241f8cfe..492338df1 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java @@ -51,7 +51,7 @@ public class ShellConnectST { @RegisterExtension - static HiveMQ hivemq = HiveMQ.builder().build(); + private final static HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension final MqttCliShell mqttCliShell = new MqttCliShell(); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java index 491072b91..1d033156d 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java @@ -34,7 +34,7 @@ public class ShellConnectTlsST { @RegisterExtension - static HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); + private final static HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); @RegisterExtension final MqttCliShell mqttCliShell = new MqttCliShell(); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java index 181213862..b939e274c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java @@ -34,7 +34,7 @@ public class ShellConnectWebsocketsST { @RegisterExtension - static HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); + private final static HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); @RegisterExtension final MqttCliShell mqttCliShell = new MqttCliShell(); diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index 09c50618c..0e8c4ae75 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -29,8 +29,12 @@ import com.hivemq.extension.sdk.api.interceptor.disconnect.DisconnectInboundInterceptor; import com.hivemq.extension.sdk.api.interceptor.disconnect.parameter.DisconnectInboundInput; import com.hivemq.extension.sdk.api.interceptor.disconnect.parameter.DisconnectInboundOutput; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundInput; +import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundOutput; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; +import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; @@ -61,6 +65,7 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCal private EmbeddedHiveMQ hivemq; private List connectPackets; + private List publishPackets; private List disconnectInformations; private int port = -1; @@ -84,6 +89,7 @@ public void beforeAll(final ExtensionContext context) throws IOException { this.port = generatePort(); this.connectPackets = new ArrayList<>(); + this.publishPackets = new ArrayList<>(); this.disconnectInformations = new ArrayList<>(); final String tlsConfig = setupTls(); @@ -122,9 +128,14 @@ public void extensionStart( final @NotNull ExtensionStartOutput extensionStartOutput) { final ConnectInboundInterceptor connectInboundInterceptor = (connectInboundInput, connectInboundOutput) -> connectPackets.add(connectInboundInput.getConnectPacket()); Services.interceptorRegistry().setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); - Services.initializerRegistry().setClientInitializer((initializerInput, clientContext) -> clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> { - disconnectInformations.add(new DisconnectInformation(disconnectInboundInput.getDisconnectPacket(), disconnectInboundInput.getClientInformation().getClientId())); - })); + Services.initializerRegistry().setClientInitializer((initializerInput, clientContext) -> { + clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> { + disconnectInformations.add(new DisconnectInformation(disconnectInboundInput.getDisconnectPacket(), disconnectInboundInput.getClientInformation().getClientId())); + }); + clientContext.addPublishInboundInterceptor((publishInboundInput, publishInboundOutput) -> { + publishPackets.add(publishInboundInput.getPublishPacket()); + }); + }); } @Override @@ -164,6 +175,10 @@ public void afterEach(final ExtensionContext context) { return connectPackets; } + public @NotNull List getPublishPackets() { + return publishPackets; + } + public @NotNull List getDisconnectInformations() { return disconnectInformations; } diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/PublishAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/PublishAssertion.java new file mode 100644 index 000000000..80504d6d8 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/PublishAssertion.java @@ -0,0 +1,104 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils.assertions; + +import com.google.common.collect.ImmutableList; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.general.UserProperties; +import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; +import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; +import org.jetbrains.annotations.NotNull; + +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PublishAssertion { + + private @NotNull Qos qos = Qos.AT_MOST_ONCE; + private boolean retain = false; + private @NotNull String topic = ""; + private @NotNull Optional payload = Optional.empty(); + private @NotNull Optional correlationData = Optional.empty(); + private @NotNull Optional contentType = Optional.empty(); + private @NotNull Optional responseTopic = Optional.empty(); + private @NotNull Optional messageExpiryInterval = Optional.of(4294967296L); + private @NotNull Optional payloadFormatIndicator = Optional.empty(); + private @NotNull UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.builder().build()); + + private PublishAssertion() { + } + + public static void assertPublishPacket(final @NotNull PublishPacket publishPacket, final @NotNull Consumer publishAssertionConsumer) { + final PublishAssertion publishAssertion = new PublishAssertion(); + publishAssertionConsumer.accept(publishAssertion); + + assertEquals(publishAssertion.qos, publishPacket.getQos()); + assertEquals(publishAssertion.retain, publishPacket.getRetain()); + assertEquals(publishAssertion.topic, publishPacket.getTopic()); + assertEquals(publishAssertion.payload, publishPacket.getPayload()); + assertEquals(publishAssertion.correlationData, publishPacket.getCorrelationData()); + assertEquals(publishAssertion.contentType, publishPacket.getContentType()); + assertEquals(publishAssertion.responseTopic, publishPacket.getResponseTopic()); + assertEquals(publishAssertion.messageExpiryInterval, publishPacket.getMessageExpiryInterval()); + assertEquals(publishAssertion.payloadFormatIndicator, publishPacket.getPayloadFormatIndicator()); + assertEquals(publishAssertion.userProperties, publishPacket.getUserProperties()); + } + + public void setQos(final @NotNull Qos qos) { + this.qos = qos; + } + + public void setRetain(final boolean retain) { + this.retain = retain; + } + + public void setTopic(final @NotNull String topic) { + this.topic = topic; + } + + public void setPayload(final @NotNull ByteBuffer payload) { + this.payload = Optional.of(payload); + } + + public void setCorrelationData(final @NotNull ByteBuffer correlationData) { + this.correlationData = Optional.of(correlationData); + } + + public void setContentType(final String contentType) { + this.contentType = Optional.of(contentType); + } + + public void setResponseTopic(final @NotNull String responseTopic) { + this.responseTopic = Optional.of(responseTopic); + } + + public void setMessageExpiryInterval(final long messageExpiryInterval) { + this.messageExpiryInterval = Optional.of(messageExpiryInterval); + } + + public void setPayloadFormatIndicator(final @NotNull PayloadFormatIndicator payloadFormatIndicator) { + this.payloadFormatIndicator = Optional.of(payloadFormatIndicator); + } + + public void setUserProperties(final @NotNull UserProperties userProperties) { + this.userProperties = userProperties; + } +} From f9d2ac0ce184d94c16b44750872dd099696de572 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 18:17:12 +0200 Subject: [PATCH 39/64] System Tests > Fix tests --- .../java/com/hivemq/cli/commands/cli/PublishST.java | 2 +- .../java/com/hivemq/cli/commands/cli/SubscribeST.java | 2 +- .../com/hivemq/cli/commands/cli/shell/ShellPublishST.java | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index 029f67af6..8998c7d0b 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -40,7 +40,7 @@ public class PublishST { @RegisterExtension - private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); private final @NotNull MqttCli mqttCli = new MqttCli(); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java index 688633749..60cd4052b 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java @@ -41,7 +41,7 @@ public class SubscribeST { @RegisterExtension - private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); private final @NotNull MqttCli mqttCli = new MqttCli(); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java index d70cc3fe8..d0932523d 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java @@ -18,16 +18,12 @@ import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.testcontainers.utility.DockerImageName; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; From 8ae69cc29d677e1d7b05459d7f95d3c8e8472cd2 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 10 Oct 2022 18:17:49 +0200 Subject: [PATCH 40/64] System Tests > Fix tests --- .../com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java index 13df862ab..7ceacea1c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java @@ -35,7 +35,7 @@ public class ShellSubscribeST { @RegisterExtension - private final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); From d9af5edb43b349338c7748c1548d7f51541a8b54 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Mon, 10 Oct 2022 21:13:30 +0200 Subject: [PATCH 41/64] System Test > Add shell publish tests --- .../commands/cli/shell/ShellPublishST.java | 333 +++++++++++++++++- .../java/com/hivemq/cli/utils/HiveMQ.java | 1 + 2 files changed, 329 insertions(+), 5 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java index d0932523d..1b26667bd 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java @@ -16,8 +16,15 @@ package com.hivemq.cli.commands.cli.shell; +import com.google.common.collect.ImmutableList; +import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; +import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -27,10 +34,14 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ShellPublishST { @@ -43,10 +54,13 @@ public class ShellPublishST { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_successful_publish(final char mqttVersion) throws Exception { + void test_successfulPublish(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test"); mqttCliShell.connectClient(hivemq, mqttVersion); - mqttCliShell.executeAsync(publishCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(publishCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending PUBLISH") + .awaitLog("received PUBLISH acknowledgement"); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); @@ -57,7 +71,316 @@ void test_successful_publish(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_publish_missing_topic(final char mqttVersion) throws Exception { + void test_messageFromFile(final char mqttVersion) throws Exception { + final Path publishFile = Files.createTempFile("publish", "txt"); + Files.write(publishFile, "message".getBytes(StandardCharsets.UTF_8)); + final List publishCommand = List.of("pub", "-t", "test", "-m:file", publishFile.toString()); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(publishCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending PUBLISH") + .awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopics(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test1", "-t", "test2", "-t", "test3", "-m", "test"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(publishCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending PUBLISH") + .awaitLog("received PUBLISH acknowledgement"); + + final PublishPacket publishPacket1 = hivemq.getPublishPackets().get(0); + final PublishPacket publishPacket2 = hivemq.getPublishPackets().get(1); + final PublishPacket publishPacket3 = hivemq.getPublishPackets().get(2); + final Set topicSet = Set.of(publishPacket1.getTopic(), publishPacket2.getTopic(), publishPacket3.getTopic()); + assertTrue(topicSet.containsAll(List.of("test1", "test2", "test3"))); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic(publishPacket1.getTopic()); + }); + + assertPublishPacket(hivemq.getPublishPackets().get(1), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic(publishPacket2.getTopic()); + }); + + assertPublishPacket(hivemq.getPublishPackets().get(2), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic(publishPacket3.getTopic()); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_qos(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-q", "1"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(publishCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending PUBLISH") + .awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + publishAssertion.setQos(Qos.AT_LEAST_ONCE); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopicsAndMultipleQos(final char mqttVersion) throws Exception { + final List publishCommand = List.of( + "pub", + "-t", "test1", + "-t", "test2", + "-t", "test3", + "-q", "0", + "-q", "1", + "-q", "2", + "-m", "test" + ); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(publishCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending PUBLISH") + .awaitLog("received PUBLISH acknowledgement"); + + final PublishPacket publishPacket1 = hivemq.getPublishPackets() + .stream() + .filter(publish -> publish.getTopic().equals("test1")) + .findFirst() + .get(); + final PublishPacket publishPacket2 = hivemq.getPublishPackets() + .stream() + .filter(publish -> publish.getTopic().equals("test2")) + .findFirst() + .get(); + final PublishPacket publishPacket3 = hivemq.getPublishPackets() + .stream() + .filter(publish -> publish.getTopic().equals("test3")) + .findFirst() + .get(); + final Set topicSet = Set.of(publishPacket1.getTopic(), publishPacket2.getTopic(), publishPacket3.getTopic()); + assertTrue(topicSet.containsAll(List.of("test1", "test2", "test3"))); + + assertPublishPacket(publishPacket1, publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic(publishPacket1.getTopic()); + publishAssertion.setQos(Qos.AT_MOST_ONCE); + }); + + assertPublishPacket(publishPacket2, publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic(publishPacket2.getTopic()); + publishAssertion.setQos(Qos.AT_LEAST_ONCE); + }); + + assertPublishPacket(publishPacket3, publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic(publishPacket3.getTopic()); + publishAssertion.setQos(Qos.EXACTLY_ONCE); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_retain(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-r"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(publishCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending PUBLISH") + .awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + publishAssertion.setRetain(true); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_messageExpiryInterval(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-e", "120"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Publish message expiry was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Publish message expiry was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending PUBLISH"); + awaitOutput.awaitLog("received PUBLISH acknowledgement"); + } else { + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending PUBLISH"); + awaitOutput.awaitLog("received PUBLISH acknowledgement"); + awaitOutput.awaitLog("messageExpiryInterval=120"); + } + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + if (mqttVersion == '5') { + publishAssertion.setMessageExpiryInterval(120); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_payloadFormatIndicator(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-pf", "utf8"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Publish payload format indicator was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Publish payload format indicator was set but is unused in MQTT Version MQTT_3_1_1"); + } + + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending PUBLISH"); + awaitOutput.awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + if (mqttVersion == '5') { + publishAssertion.setPayloadFormatIndicator(PayloadFormatIndicator.UTF_8); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_contentType(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-ct", "my-content"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Publish content type was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Publish content type was set but is unused in MQTT Version MQTT_3_1_1"); + } + + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending PUBLISH"); + awaitOutput.awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + if (mqttVersion == '5') { + publishAssertion.setContentType("my-content"); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_responseTopic(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-rt", "response-topic"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Publish response topic was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Publish response topic was set but is unused in MQTT Version MQTT_3_1_1"); + } + + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending PUBLISH"); + awaitOutput.awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + if (mqttVersion == '5') { + publishAssertion.setResponseTopic("response-topic"); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_correlationData(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-cd", "correlation-data"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Publish correlation data was set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Publish correlation data was set but is unused in MQTT Version MQTT_3_1_1"); + } + + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending PUBLISH"); + awaitOutput.awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + if (mqttVersion == '5') { + publishAssertion.setCorrelationData(ByteBuffer.wrap("correlation-data".getBytes(StandardCharsets.UTF_8))); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userProperties(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-up", "key1=value1", "-up", "key2=value2"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Publish user properties were set but is unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Publish user properties were set but is unused in MQTT Version MQTT_3_1_1"); + } + + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending PUBLISH"); + awaitOutput.awaitLog("received PUBLISH acknowledgement"); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + if (mqttVersion == '5') { + final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.builder() + .add(new MqttUserProperty("key1", "value1")) + .add(new MqttUserProperty("key2", "value2")) + .build()); + publishAssertion.setUserProperties(expectedUserProperties); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_publishMissingTopic(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub"); mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(publishCommand) @@ -68,7 +391,7 @@ void test_publish_missing_topic(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_publish_missing_message(final char mqttVersion) throws Exception { + void test_publishMissingMessage(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test"); mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(publishCommand) @@ -78,7 +401,7 @@ void test_publish_missing_message(final char mqttVersion) throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_missing_arguments() throws Exception { + void test_missingArguments() throws Exception { final List publishCommand = List.of("pub"); mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Unmatched argument at index 0: 'pub'") diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index 0e8c4ae75..cb6549ddc 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -169,6 +169,7 @@ public void afterAll(final ExtensionContext context) { public void afterEach(final ExtensionContext context) { connectPackets.clear(); disconnectInformations.clear(); + publishPackets.clear(); } public @NotNull List getConnectPackets() { From c7f1794b40cc516f6da9be9cdc7931d496603ce8 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Tue, 11 Oct 2022 00:32:16 +0200 Subject: [PATCH 42/64] System Test > Add shell subscribe tests --- .../hivemq/cli/commands/cli/PublishST.java | 42 ++- .../commands/cli/shell/ShellPublishST.java | 7 +- .../commands/cli/shell/ShellSubscribeST.java | 356 +++++++++++++++++- .../cli/shell/connect/ShellConnectST.java | 2 + .../java/com/hivemq/cli/utils/HiveMQ.java | 25 +- .../utils/assertions/SubscribeAssertion.java | 51 +++ 6 files changed, 459 insertions(+), 24 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index 8998c7d0b..2fdb10e15 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -21,19 +21,17 @@ import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; -import org.testcontainers.utility.DockerImageName; +import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -75,6 +73,42 @@ void test_successful_publish() throws Exception { assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); } + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_emptyMessage() throws Exception { + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttPort()), + "-t", "test", + "-m:empty", + "-d" + ); + + final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() + .identifier("subscriber") + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .buildBlocking(); + subscriber.connect(); + final CountDownLatch receivedPublish = new CountDownLatch(1); + subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> receivedPublish.countDown()).send(); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertTrue(receivedPublish.await(10, TimeUnit.SECONDS)); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); + assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); + assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.allocate(0)); + }); + } + @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_topic() throws Exception { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java index 1b26667bd..e205dd904 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java @@ -63,8 +63,11 @@ void test_successfulPublish(final char mqttVersion) throws Exception { .awaitLog("received PUBLISH acknowledgement"); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { - publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); - publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + publishAssertion.setTopic("test"); + if (mqttVersion == '5') { + publishAssertion.setMessageExpiryInterval(120); + } }); } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java index 7ceacea1c..571b344b8 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java @@ -16,22 +16,36 @@ package com.hivemq.cli.commands.cli.shell; +import com.google.common.collect.ImmutableList; +import com.google.gson.JsonObject; +import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; +import com.hivemq.client.mqtt.MqttClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; +import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.extensions.packets.subscribe.SubscriptionImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.testcontainers.utility.DockerImageName; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Base64; import java.util.List; import java.util.concurrent.TimeUnit; +import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class ShellSubscribeST { @RegisterExtension @@ -43,16 +57,342 @@ public class ShellSubscribeST { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_successful_subscribe(final char mqttVersion) throws Exception { + void test_successfulSubscribe(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test"); mqttCliShell.connectClient(hivemq, mqttVersion); - mqttCliShell.executeAsync(subscribeCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopics(final char mqttVersion) throws Exception { + //FIXME Subscribe command should susbscribe to all topics in one packet and not send separate subscribes + final List subscribeCommand = List.of("sub", "-t", "test1", "-t", "test2", "-t", "test3"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK") + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK") + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("test1", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("test2", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("test3", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { + //FIXME Subscribe command should susbscribe to all topics in one packet and not send separate subscribes + final List subscribeCommand = + List.of("sub", "-t", "test1", "-t", "test2", "-t", "test3", "-q", "0", "-q", "1", "-q", "2"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK") + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK") + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("test1", Qos.AT_MOST_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("test2", Qos.AT_LEAST_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("test3", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userProperties(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test", "-up", "key1=value1", "-up", "key2=value2"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Subscribe user properties were set but are unused in MQTT version MQTT_3_1_1"); + awaitOutput.awaitLog("Subscribe user properties were set but are unused in MQTT version MQTT_3_1_1"); + } + + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending SUBSCRIBE"); + awaitOutput.awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)) + ); + + if (mqttVersion == '5') { + final UserPropertiesImpl userProperties = UserPropertiesImpl.of(ImmutableList.of( + MqttUserProperty.of("key1", "value1"), + MqttUserProperty.of("key2", "value2")) + ); + subscribeAssertion.setUserProperties(userProperties); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_stay(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test", "-s"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false))); + }); + + final Mqtt5BlockingClient publisher = MqttClient.builder() + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + publisher.connect(); + publisher.publishWith().topic("test").payload("message".getBytes(StandardCharsets.UTF_8)).send(); + + awaitOutput.awaitStdOut("message"); + awaitOutput.awaitLog("received PUBLISH ('message')"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_outputToConsole(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test", "-oc"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false))); + }); + + final Mqtt5BlockingClient publisher = MqttClient.builder() + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + publisher.connect(); + publisher.publishWith().topic("test").payload("message".getBytes(StandardCharsets.UTF_8)).send(); + + awaitOutput.awaitStdOut("message"); + awaitOutput.awaitLog("received PUBLISH ('message')"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_outputToFile(final char mqttVersion) throws Exception { + final Path outputFile = Files.createTempFile("publishes", ".txt"); + outputFile.toFile().deleteOnExit(); + final List subscribeCommand = List.of("sub", "-t", "test", "-of", outputFile.toString()); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false))); + }); + + final Mqtt5BlockingClient publisher = MqttClient.builder() + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + publisher.connect(); + publisher.publishWith().topic("test").payload("message".getBytes(StandardCharsets.UTF_8)).send(); + + awaitOutput.awaitLog("received PUBLISH ('message')"); + + final List readLines = Files.readAllLines(outputFile); + assertEquals(1, readLines.size()); + assertEquals("message", readLines.get(0)); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_base64(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test", "-s", "-b64"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false))); + }); + + final Mqtt5BlockingClient publisher = MqttClient.builder() + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + publisher.connect(); + publisher.publishWith().topic("test").payload("message".getBytes(StandardCharsets.UTF_8)).send(); + + final String encodedPayload = Base64.getEncoder().encodeToString("message".getBytes(StandardCharsets.UTF_8)); + awaitOutput.awaitStdOut(encodedPayload); + awaitOutput.awaitLog("received PUBLISH ('message')"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_showTopics(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test", "-s", "-T"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false))); + }); + + final Mqtt5BlockingClient publisher = MqttClient.builder() + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + publisher.connect(); + publisher.publishWith().topic("test").payload("message".getBytes(StandardCharsets.UTF_8)).send(); + + awaitOutput.awaitStdOut("test: message"); + awaitOutput.awaitLog("received PUBLISH ('message')"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_Json(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", "-t", "test", "-s", "-J"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) + .awaitLog("sending SUBSCRIBE") + .awaitLog("received SUBACK"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( + "test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false))); + }); + + final Mqtt5BlockingClient publisher = MqttClient.builder() + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + publisher.connect(); + final JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("property1", "value1"); + jsonObject.addProperty("property2", "value2"); + jsonObject.addProperty("property3", "value3"); + + publisher.publishWith().topic("test").payload(jsonObject.toString().getBytes(StandardCharsets.UTF_8)).send(); + + awaitOutput.awaitStdOut("{\n" + + " \"topic\": \"test\",\n" + + " \"payload\": {\n" + + " \"property1\": \"value1\",\n" + + " \"property2\": \"value2\",\n" + + " \"property3\": \"value3\"\n" + + " },\n"); + awaitOutput.awaitStdOut("\"qos\": \"AT_MOST_ONCE\","); + awaitOutput.awaitStdOut("\"receivedAt\":"); + awaitOutput.awaitStdOut("\"retain\": false"); + awaitOutput.awaitStdOut("}"); + awaitOutput.awaitLog("received PUBLISH"); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_subscribe_missing_topic(final char mqttVersion) throws Exception{ + void test_subscribeMissingTopic(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub"); mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) @@ -62,7 +402,7 @@ void test_subscribe_missing_topic(final char mqttVersion) throws Exception{ @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_missing_arguments() throws Exception { + void test_missingArguments() throws Exception { final List subscribeCommand = List.of("sub"); mqttCliShell.executeAsync(subscribeCommand) .awaitStdOut("mqtt>") diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java index 492338df1..910ab5ed7 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java @@ -409,6 +409,7 @@ void test_userNameAndPassword(final char mqttVersion) throws Exception { void test_passwordFile(final char mqttVersion) throws Exception { final Path passwordFile = Files.createTempFile("mqtt-cli-password", ".txt"); + passwordFile.toFile().deleteOnExit(); Files.writeString(passwordFile, "password", StandardCharsets.UTF_8); final List connectCommand = defaultConnectCommand(mqttVersion); @@ -438,6 +439,7 @@ void test_passwordFile(final char mqttVersion) throws Exception { void test_userNameAndPasswordFile(final char mqttVersion) throws Exception { final Path passwordFile = Files.createTempFile("mqtt-cli-password", ".txt"); + passwordFile.toFile().deleteOnExit(); Files.writeString(passwordFile, "password", StandardCharsets.UTF_8); final List connectCommand = defaultConnectCommand(mqttVersion); diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index cb6549ddc..b4a55ee22 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -23,24 +23,15 @@ import com.hivemq.embedded.EmbeddedHiveMQBuilder; import com.hivemq.extension.sdk.api.ExtensionMain; import com.hivemq.extension.sdk.api.annotations.NotNull; -import com.hivemq.extension.sdk.api.client.ClientContext; -import com.hivemq.extension.sdk.api.client.parameter.InitializerInput; import com.hivemq.extension.sdk.api.interceptor.connect.ConnectInboundInterceptor; -import com.hivemq.extension.sdk.api.interceptor.disconnect.DisconnectInboundInterceptor; -import com.hivemq.extension.sdk.api.interceptor.disconnect.parameter.DisconnectInboundInput; -import com.hivemq.extension.sdk.api.interceptor.disconnect.parameter.DisconnectInboundOutput; -import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; -import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundInput; -import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundOutput; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; -import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; +import com.hivemq.extension.sdk.api.packets.subscribe.SubscribePacket; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; -import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; import com.hivemq.migration.meta.PersistenceType; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; @@ -66,6 +57,7 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCal private List connectPackets; private List publishPackets; + private List subscribePackets; private List disconnectInformations; private int port = -1; @@ -91,6 +83,7 @@ public void beforeAll(final ExtensionContext context) throws IOException { this.connectPackets = new ArrayList<>(); this.publishPackets = new ArrayList<>(); this.disconnectInformations = new ArrayList<>(); + this.subscribePackets = new ArrayList<>(); final String tlsConfig = setupTls(); final String websocketsConfig = setupWebsockets(); @@ -108,11 +101,13 @@ public void beforeAll(final ExtensionContext context) throws IOException { ""; final Path hivemqConfigFolder = Files.createTempDirectory("hivemq-config-folder"); + hivemqConfigFolder.toFile().deleteOnExit(); final File configXml = new File(hivemqConfigFolder.toAbsolutePath().toString(), "config.xml"); assertTrue(configXml.createNewFile()); Files.writeString(configXml.toPath(), hivemqConfig); final Path hivemqDataFolder = Files.createTempDirectory("hivemq-data-folder"); + hivemqDataFolder.toFile().deleteOnExit(); final EmbeddedExtension embeddedExtension = EmbeddedExtension.builder() .withId("test-interceptor-extension") @@ -129,12 +124,18 @@ public void extensionStart( final ConnectInboundInterceptor connectInboundInterceptor = (connectInboundInput, connectInboundOutput) -> connectPackets.add(connectInboundInput.getConnectPacket()); Services.interceptorRegistry().setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); Services.initializerRegistry().setClientInitializer((initializerInput, clientContext) -> { + clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> { disconnectInformations.add(new DisconnectInformation(disconnectInboundInput.getDisconnectPacket(), disconnectInboundInput.getClientInformation().getClientId())); }); + clientContext.addPublishInboundInterceptor((publishInboundInput, publishInboundOutput) -> { publishPackets.add(publishInboundInput.getPublishPacket()); }); + + clientContext.addSubscribeInboundInterceptor((subscribeInboundInput, subscribeInboundOutput) -> { + subscribePackets.add(subscribeInboundInput.getSubscribePacket()); + }); }); } @@ -180,6 +181,10 @@ public void afterEach(final ExtensionContext context) { return publishPackets; } + public @NotNull List getSubscribePackets() { + return subscribePackets; + } + public @NotNull List getDisconnectInformations() { return disconnectInformations; } diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java new file mode 100644 index 000000000..22d6d85ac --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java @@ -0,0 +1,51 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils.assertions; + +import com.google.common.collect.ImmutableList; +import com.hivemq.extension.sdk.api.packets.general.UserProperties; +import com.hivemq.extension.sdk.api.packets.subscribe.SubscribePacket; +import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SubscribeAssertion { + + private @NotNull List subscriptions = List.of(); + private @NotNull UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.of()); + private SubscribeAssertion() { + } + + public static void assertSubscribePacket(final @NotNull SubscribePacket subscribePacket, final @NotNull Consumer subscribeAssertionConsumer) { + final SubscribeAssertion subscribeAssertion = new SubscribeAssertion(); + subscribeAssertionConsumer.accept(subscribeAssertion); + assertEquals(subscribeAssertion.subscriptions, subscribePacket.getSubscriptions()); + assertEquals(subscribeAssertion.userProperties, subscribePacket.getUserProperties()); + } + + public void setSubscriptions(final @NotNull List subscriptions) { + this.subscriptions = subscriptions; + } + + public void setUserProperties(final @NotNull UserProperties userProperties) { + this.userProperties = userProperties; + } +} From ee1975bfba650b3f27ecb16db86691089898f873 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Tue, 11 Oct 2022 00:49:19 +0200 Subject: [PATCH 43/64] System Test > Fix tests --- .../hivemq/cli/commands/cli/PublishST.java | 56 +++++-------------- .../commands/cli/shell/ShellPublishST.java | 3 - .../cli/shell/connect/ShellConnectST.java | 1 + 3 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index 2fdb10e15..d3be4ad94 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -17,71 +17,50 @@ package com.hivemq.cli.commands.cli; import com.hivemq.cli.utils.ExecutionResult; -import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.extension.RegisterExtension; +import org.testcontainers.utility.DockerImageName; -import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class PublishST { - @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQTestContainerExtension hivemq = + new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); private final @NotNull MqttCli mqttCli = new MqttCli(); - @Test - @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_publish() throws Exception { - final List publishCommand = List.of( - "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-t", "test", - "-m", "test", - "-d" - ); - - final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() - .identifier("subscriber") - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) - .buildBlocking(); - subscriber.connect(); - final CountDownLatch receivedPublish = new CountDownLatch(1); - subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> receivedPublish.countDown()).send(); - - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + @BeforeAll + static void beforeAll() { + hivemq.start(); + } - assertTrue(receivedPublish.await(10, TimeUnit.SECONDS)); - assertEquals(0, executionResult.getExitCode()); - assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); - assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); - assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); - assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); + @AfterAll + static void afterAll() { + hivemq.stop(); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_emptyMessage() throws Exception { + void test_successful_publish() throws Exception { final List publishCommand = List.of( "pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test", - "-m:empty", + "-m", "test", "-d" ); @@ -102,11 +81,6 @@ void test_emptyMessage() throws Exception { assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); - - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { - publishAssertion.setTopic("test"); - publishAssertion.setPayload(ByteBuffer.allocate(0)); - }); } @Test diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java index e205dd904..ed714cfb8 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java @@ -65,9 +65,6 @@ void test_successfulPublish(final char mqttVersion) throws Exception { assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); - if (mqttVersion == '5') { - publishAssertion.setMessageExpiryInterval(120); - } }); } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java index 910ab5ed7..a5d6039ba 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java @@ -568,6 +568,7 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setCleanStart(false); connectAssertion.setClientId("sessionTest"); }); From 705630ae2896393542a805f38306aaace099e1ea Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 11 Oct 2022 10:41:40 +0200 Subject: [PATCH 44/64] System Tests > Add shell unsubscribe tests --- .../hivemq/cli/mqtt/MqttClientExecutor.java | 2 +- .../cli/shell/ShellUnsubscribeST.java | 128 ++++++++++++++++++ .../java/com/hivemq/cli/utils/HiveMQ.java | 17 +++ .../assertions/UnsubscribeAssertion.java | 40 ++++++ 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java diff --git a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java index a99d739a0..1fe7c9147 100644 --- a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java +++ b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java @@ -280,7 +280,7 @@ void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull Unsubscr for (final String topic : unsubscribe.getTopics()) { final Mqtt3Unsubscribe unsubscribeMessage = Mqtt3Unsubscribe.builder().topicFilter(topic).build(); - Logger.debug("{} Sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); + Logger.debug("{} sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); client.toAsync().unsubscribe(unsubscribeMessage).whenComplete((Void unsubAck, Throwable throwable) -> { if (throwable != null) { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java new file mode 100644 index 000000000..0ab74fc2d --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java @@ -0,0 +1,128 @@ +package com.hivemq.cli.commands.cli.shell; + +import com.google.common.collect.ImmutableList; +import com.hivemq.cli.utils.AwaitOutput; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.assertions.UnsubscribeAssertion.assertUnsubscribePacket; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ShellUnsubscribeST { + + @RegisterExtension + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + + @RegisterExtension + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_successfulUnsubscribe(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("unsub", "-t", "test"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending UNSUBSCRIBE") + .awaitLog("received UNSUBACK"); + + assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(0), unsubscribeAssertion -> { + unsubscribeAssertion.setTopicFilters(List.of("test")); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopics(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("unsub", "-t", "test1", "-t", "test2", "-t", "test3"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitLog("sending UNSUBSCRIBE") + .awaitLog("received UNSUBACK") + .awaitLog("sending UNSUBSCRIBE") + .awaitLog("received UNSUBACK") + .awaitLog("sending UNSUBSCRIBE") + .awaitLog("received UNSUBACK"); + + + assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(0), unsubscribeAssertion -> { + unsubscribeAssertion.setTopicFilters(List.of("test1")); + }); + + assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(1), unsubscribeAssertion -> { + unsubscribeAssertion.setTopicFilters(List.of("test2")); + }); + + assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(2), unsubscribeAssertion -> { + unsubscribeAssertion.setTopicFilters(List.of("test3")); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userProperties(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("unsub", "-t", "test", "-up", "key1=value1", "-up", "key2=value2"); + mqttCliShell.connectClient(hivemq, mqttVersion); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand); + + if (mqttVersion == '3') { + awaitOutput.awaitStdErr("Unsubscribe user properties were set but are unused in MQTT Version MQTT_3_1_1"); + awaitOutput.awaitLog("Unsubscribe user properties were set but are unused in MQTT Version MQTT_3_1_1"); + } + awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitLog("sending UNSUBSCRIBE"); + awaitOutput.awaitLog("received UNSUBACK"); + + assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(0), unsubscribeAssertion -> { + unsubscribeAssertion.setTopicFilters(List.of("test")); + + if (mqttVersion == '5') { + final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.builder() + .add(MqttUserProperty.of("key1", "value1")) + .add(MqttUserProperty.of("key2", "value2")) + .build()); + unsubscribeAssertion.setUserProperties(expectedUserProperties); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_missingTopic(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("unsub"); + mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdErr("Missing required option '--topic '") + .awaitStdErr("Try 'help unsub' for more information."); + + assertEquals(0, hivemq.getUnsubscribePackets().size()); + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_UnsubscribeWhileNotConnected() throws Exception { + final List subscribeCommand = List.of("unsub"); + mqttCliShell.executeAsync(subscribeCommand) + .awaitStdErr("Unmatched argument at index 0: 'unsub'") + .awaitStdErr("Try 'help' to get a list of commands."); + + assertEquals(0, hivemq.getUnsubscribePackets().size()); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index b4a55ee22..cf190cb87 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -24,9 +24,13 @@ import com.hivemq.extension.sdk.api.ExtensionMain; import com.hivemq.extension.sdk.api.annotations.NotNull; import com.hivemq.extension.sdk.api.interceptor.connect.ConnectInboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.unsubscribe.UnsubscribeInboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.unsubscribe.parameter.UnsubscribeInboundInput; +import com.hivemq.extension.sdk.api.interceptor.unsubscribe.parameter.UnsubscribeInboundOutput; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; import com.hivemq.extension.sdk.api.packets.subscribe.SubscribePacket; +import com.hivemq.extension.sdk.api.packets.unsubscribe.UnsubscribePacket; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; @@ -58,6 +62,7 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCal private List connectPackets; private List publishPackets; private List subscribePackets; + private List unsubscribePackets; private List disconnectInformations; private int port = -1; @@ -84,6 +89,7 @@ public void beforeAll(final ExtensionContext context) throws IOException { this.publishPackets = new ArrayList<>(); this.disconnectInformations = new ArrayList<>(); this.subscribePackets = new ArrayList<>(); + this.unsubscribePackets = new ArrayList<>(); final String tlsConfig = setupTls(); final String websocketsConfig = setupWebsockets(); @@ -136,6 +142,11 @@ public void extensionStart( clientContext.addSubscribeInboundInterceptor((subscribeInboundInput, subscribeInboundOutput) -> { subscribePackets.add(subscribeInboundInput.getSubscribePacket()); }); + + clientContext.addUnsubscribeInboundInterceptor((unsubscribeInboundInput, unsubscribeInboundOutput) -> { + unsubscribePackets.add(unsubscribeInboundInput.getUnsubscribePacket()); + }); + }); } @@ -171,6 +182,8 @@ public void afterEach(final ExtensionContext context) { connectPackets.clear(); disconnectInformations.clear(); publishPackets.clear(); + subscribePackets.clear(); + unsubscribePackets.clear(); } public @NotNull List getConnectPackets() { @@ -185,6 +198,10 @@ public void afterEach(final ExtensionContext context) { return subscribePackets; } + public @NotNull List getUnsubscribePackets() { + return unsubscribePackets; + } + public @NotNull List getDisconnectInformations() { return disconnectInformations; } diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java new file mode 100644 index 000000000..6f3eaaca1 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java @@ -0,0 +1,40 @@ +package com.hivemq.cli.utils.assertions; + +import com.google.common.collect.ImmutableList; +import com.hivemq.extension.sdk.api.packets.general.UserProperties; +import com.hivemq.extension.sdk.api.packets.unsubscribe.UnsubscribePacket; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class UnsubscribeAssertion { + + private @NotNull List topicFilters = List.of(); + private @NotNull UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.of()); + + private UnsubscribeAssertion() { + } + + public static void assertUnsubscribePacket( + final @NotNull UnsubscribePacket unsubscribePacket, + final @NotNull Consumer unsubscribeAssertionConsumer) { + + final UnsubscribeAssertion unsubscribeAssertion = new UnsubscribeAssertion(); + unsubscribeAssertionConsumer.accept(unsubscribeAssertion); + + assertEquals(unsubscribeAssertion.topicFilters, unsubscribePacket.getTopicFilters()); + assertEquals(unsubscribeAssertion.userProperties, unsubscribePacket.getUserProperties()); + } + + public void setTopicFilters(final @NotNull List topicFilters) { + this.topicFilters = topicFilters; + } + + public void setUserProperties(final @NotNull UserProperties userProperties) { + this.userProperties = userProperties; + } +} From a0f7d07ada1f647b532726f7a1ec5b703c523c13 Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 11 Oct 2022 10:50:18 +0200 Subject: [PATCH 45/64] System Tests > Add shell switch tests --- .../cli/commands/cli/shell/ShellSwitchST.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java new file mode 100644 index 000000000..03c2f7c70 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java @@ -0,0 +1,61 @@ +package com.hivemq.cli.commands.cli.shell; + +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliShell; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class ShellSwitchST { + @RegisterExtension + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + + @RegisterExtension + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_successfulSwitchFromContext(final char mqttVersion) throws Exception { + final List switchCommand = List.of("switch", String.format("client1@%s", hivemq.getHost())); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_successfulSwitchWithoutContext(final char mqttVersion) throws Exception { + final List switchCommand = List.of("switch", String.format("client1@%s", hivemq.getHost())); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.executeAsync(List.of("exit")).awaitStdOut("mqtt>"); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_hostAndIdentifierWithContext(final char mqttVersion) throws Exception { + final List switchCommand = List.of("switch", "-i", "client1", "-h", hivemq.getHost()); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_hostAndIdentifierWithoutContext(final char mqttVersion) throws Exception { + final List switchCommand = List.of("switch", "-i", "client1", "-h", hivemq.getHost()); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.executeAsync(List.of("exit")).awaitStdOut("mqtt>"); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + } +} From 9a988f7c80c8e9d4d8ee00c2a55a6d7b10bb5755 Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 11 Oct 2022 11:55:10 +0200 Subject: [PATCH 46/64] System Tests > Add shell ls and exit tests --- .../cli/commands/cli/shell/ShellExitST.java | 58 +++++++ .../cli/commands/cli/shell/ShellListST.java | 141 ++++++++++++++++++ .../cli/commands/cli/shell/ShellSwitchST.java | 15 ++ .../cli/shell/ShellUnsubscribeST.java | 15 ++ .../com/hivemq/cli/utils/AwaitOutput.java | 1 + .../com/hivemq/cli/utils/MqttCliShell.java | 4 + .../assertions/UnsubscribeAssertion.java | 15 ++ 7 files changed, 249 insertions(+) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellExitST.java create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellListST.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellExitST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellExitST.java new file mode 100644 index 000000000..430f40522 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellExitST.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.shell; + +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliShell; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.await; + +public class ShellExitST { + + @RegisterExtension + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + + @RegisterExtension + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_exitContext(final char mqttVersion) throws Exception { + final List exitCommand = List.of("exit"); + final List lsCommand = List.of("ls"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client"); + mqttCliShell.executeAsync(exitCommand).awaitStdOut("mqtt>"); + mqttCliShell.executeAsync(lsCommand).awaitStdOut(String.format("client@%s", hivemq.getHost())); + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_exitShell() throws Exception { + final List exitCommand = List.of("exit"); + mqttCliShell.executeAsync(exitCommand); + await().until(() -> !mqttCliShell.isAlive()); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellListST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellListST.java new file mode 100644 index 000000000..bab5bab6e --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellListST.java @@ -0,0 +1,141 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.shell; + +import com.hivemq.cli.utils.AwaitOutput; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.client.mqtt.MqttVersion; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.fail; + +public class ShellListST { + + @RegisterExtension + private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + + @RegisterExtension + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_emptyList() throws Exception { + final List listCommand = List.of("ls"); + mqttCliShell.executeAsync(listCommand).awaitStdOut("mqtt>"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_listConnectedClients(final char mqttVersion) throws Exception { + final List listCommand = List.of("ls"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + mqttCliShell.executeAsync(listCommand) + .awaitStdOut(String.format("client1@%s", hivemq.getHost())) + .awaitStdOut(String.format("client2@%s", hivemq.getHost())) + .awaitStdOut(String.format("client3@%s", hivemq.getHost())); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_listConnectedClientsReverse(final char mqttVersion) throws Exception { + final List listCommand = List.of("ls", "-r"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + mqttCliShell.executeAsync(listCommand) + .awaitStdOut(String.format("client3@%s", hivemq.getHost())) + .awaitStdOut(String.format("client2@%s", hivemq.getHost())) + .awaitStdOut(String.format("client1@%s", hivemq.getHost())); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_listLongConnectedClients(final char mqttVersion) throws Exception { + final List listCommand = List.of("ls", "-l"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); + mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(listCommand); + + awaitOutput.awaitStdOut("CONNECTED") + .awaitStdOut("client1") + .awaitStdOut(hivemq.getHost()) + .awaitStdOut(String.valueOf(hivemq.getMqttPort())) + .awaitStdOut(toVersion(mqttVersion).name()) + .awaitStdOut("NO_SSL"); + + awaitOutput.awaitStdOut("CONNECTED") + .awaitStdOut("client2") + .awaitStdOut(hivemq.getHost()) + .awaitStdOut(String.valueOf(hivemq.getMqttPort())) + .awaitStdOut(toVersion(mqttVersion).name()) + .awaitStdOut("NO_SSL"); + + awaitOutput.awaitStdOut("CONNECTED") + .awaitStdOut("client3") + .awaitStdOut(hivemq.getHost()) + .awaitStdOut(String.valueOf(hivemq.getMqttPort())) + .awaitStdOut(toVersion(mqttVersion).name()) + .awaitStdOut("NO_SSL"); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_listSubscriptions(final char mqttVersion) throws Exception { + final List listCommand = List.of("ls", "-s"); + + mqttCliShell.connectClient(hivemq, mqttVersion, "subscription-client1"); + mqttCliShell.executeAsync(List.of("sub", "-t", "topic1")) + .awaitStdOut(String.format("subscription-client1@%s>", hivemq.getHost())) + .awaitLog("received SUBACK"); + + mqttCliShell.connectClient(hivemq, mqttVersion, "subscription-client2"); + mqttCliShell.executeAsync(List.of("sub", "-t", "topic2")) + .awaitStdOut(String.format("subscription-client2@%s>", hivemq.getHost())) + .awaitLog("received SUBACK"); + + mqttCliShell.executeAsync(listCommand) + .awaitStdOut(String.format("subscription-client1@%s", hivemq.getHost())) + .awaitStdOut("-subscribed topics: [topic1]") + .awaitStdOut(String.format("subscription-client2@%s", hivemq.getHost())) + .awaitStdOut("-subscribed topics: [topic2]"); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.MQTT_3_1_1; + } else if (version == '5') { + return MqttVersion.MQTT_5_0; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java index 03c2f7c70..f3dc2c15c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.shell; import com.hivemq.cli.utils.HiveMQ; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java index 0ab74fc2d..6760357c7 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.shell; import com.google.common.collect.ImmutableList; diff --git a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java index 1d0758d4a..82bbda9af 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java +++ b/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java @@ -60,4 +60,5 @@ public AwaitOutput(final @NotNull ProcessIO processIO, final @Nullable LogWaiter } return this; } + } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index 8a260bd9c..d8e29cb64 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -168,6 +168,10 @@ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, return new AwaitOutput(processIO, logWaiter, fullCommand); } + public boolean isAlive() { + return process.isAlive(); + } + private @NotNull MqttVersion toVersion(final char version) { if (version == '3') { return MqttVersion.V_3_1_1; diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java index 6f3eaaca1..bc217cff6 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils.assertions; import com.google.common.collect.ImmutableList; From 249c99270ee2e092f473c690efd8d0d0c28bbf0b Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 11 Oct 2022 14:58:38 +0200 Subject: [PATCH 47/64] System Tests > Add publish connect tests --- .../hivemq/cli/commands/cli/PublishST.java | 663 ++++++++++++++++-- .../{cli => }/shell/ShellDisconnectST.java | 2 +- .../commands/{cli => }/shell/ShellExitST.java | 2 +- .../commands/{cli => }/shell/ShellListST.java | 2 +- .../{cli => }/shell/ShellPublishST.java | 2 +- .../{cli => }/shell/ShellSubscribeST.java | 2 +- .../{cli => }/shell/ShellSwitchST.java | 3 +- .../{cli => }/shell/ShellUnsubscribeST.java | 2 +- .../shell/connect/ShellConnectEnvST.java | 2 +- .../shell/connect/ShellConnectST.java | 5 +- .../shell/connect/ShellConnectTlsST.java | 2 +- .../connect/ShellConnectWebsocketsST.java | 2 +- .../java/com/hivemq/cli/utils/HiveMQ.java | 17 + .../java/com/hivemq/cli/utils/MqttCli.java | 44 +- 14 files changed, 685 insertions(+), 65 deletions(-) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/ShellDisconnectST.java (99%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/ShellExitST.java (98%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/ShellListST.java (99%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/ShellPublishST.java (99%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/ShellSubscribeST.java (99%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/ShellSwitchST.java (97%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/ShellUnsubscribeST.java (99%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/connect/ShellConnectEnvST.java (98%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/connect/ShellConnectST.java (99%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/connect/ShellConnectTlsST.java (98%) rename src/systemTest/java/com/hivemq/cli/commands/{cli => }/shell/connect/ShellConnectWebsocketsST.java (98%) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java index d3be4ad94..eea9bfe92 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java @@ -16,81 +16,621 @@ package com.hivemq.cli.commands.cli; +import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; +import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; +import com.hivemq.extension.sdk.api.services.builder.Builders; +import com.hivemq.extension.sdk.api.services.builder.WillPublishBuilder; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.utility.DockerImageName; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static org.junit.jupiter.api.Assertions.*; public class PublishST { - private static final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")); + @RegisterExtension + private static final HiveMQ hivemq = HiveMQ.builder().build(); private final @NotNull MqttCli mqttCli = new MqttCli(); - @BeforeAll - static void beforeAll() { - hivemq.start(); + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_successfulConnectAndPublish(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertPublishOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); + }); } - @AfterAll - static void afterAll() { - hivemq.stop(); + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectWrongHost(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", + "-h", + "wrong-host", + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + String.valueOf(mqttVersion), + "-t", + "test", + "-m", + "test", + "-d"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput() + .contains("wrong-host: nodename nor servname provided, or not known")); } - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_publish() throws Exception { - final List publishCommand = List.of( - "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-t", "test", - "-m", "test", - "-d" - ); + @ValueSource(chars = {'3', '5'}) + void test_connectWrongPort(final char mqttVersion) throws Exception { + final List publishCommand = List.of("pub", + "-h", + hivemq.getHost(), + "-p", + "22", + "-V", + String.valueOf(mqttVersion), + "-t", + "test", + "-m", + "test", + "-d"); - final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() - .identifier("subscriber") - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) - .buildBlocking(); - subscriber.connect(); - final CountDownLatch receivedPublish = new CountDownLatch(1); - subscriber.toAsync().subscribeWith().topicFilter("test").callback(ignored -> receivedPublish.countDown()).send(); + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Connection refused")); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectCleanStart(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--no-cleanStart"); final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); - assertTrue(receivedPublish.await(10, TimeUnit.SECONDS)); - assertEquals(0, executionResult.getExitCode()); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setCleanStart(false); + if (mqttVersion == '3') { + connectAssertion.setSessionExpiryInterval(4294967295L); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectKeepAlive(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-k"); + publishCommand.add("100"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setKeepAlive(100); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectNoClientId(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.remove("-i"); + publishCommand.remove("cliTest"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); - assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); - assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); + + final String expectedClientId; + if (mqttVersion == '5') { + final ConnackPacket connackPacket = hivemq.getConnackPackets().get(0); + assertTrue(connackPacket.getAssignedClientIdentifier().isPresent()); + expectedClientId = connackPacket.getAssignedClientIdentifier().get(); + } else { + final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + expectedClientId = connectPacket.getClientId(); + } + + assertTrue(executionResult.getStandardOutput() + .contains(String.format("Client '%s@%s' sending PUBLISH", expectedClientId, hivemq.getHost()))); + assertTrue(executionResult.getStandardOutput() + .contains(String.format("Client '%s@%s' received PUBLISH acknowledgement", + expectedClientId, + hivemq.getHost()))); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId(expectedClientId); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectIdentifierPrefix(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.remove("-i"); + publishCommand.remove("cliTest"); + publishCommand.add("-ip"); + publishCommand.add("test-"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + if (mqttVersion == '3') { + assertTrue(connectPacket.getClientId().startsWith("test-")); + } + + assertConnectPacket(connectPacket, connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId(connectPacket.getClientId()); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserName(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-u"); + publishCommand.add("username"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectPassword(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-pw"); + publishCommand.add("password"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Password-Only Authentication is not allowed in MQTT 3")); + assertEquals(0, executionResult.getExitCode()); + } else { + assertPublishOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectPasswordEnv(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-pw:env"); + publishCommand.add("PASSWORD"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand, Map.of("PASSWORD", "password")); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Password-Only Authentication is not allowed in MQTT 3")); + assertEquals(0, executionResult.getExitCode()); + } else { + assertPublishOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectPasswordFile(final char mqttVersion) throws Exception { + final Path passwordFile = Files.createTempFile("password-file", ".txt"); + passwordFile.toFile().deleteOnExit(); + Files.writeString(passwordFile, "password"); + + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-pw:file"); + publishCommand.add(passwordFile.toString()); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Password-Only Authentication is not allowed in MQTT 3")); + assertEquals(0, executionResult.getExitCode()); + } else { + assertPublishOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserNameAndPassword(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-u"); + publishCommand.add("username"); + publishCommand.add("-pw"); + publishCommand.add("password"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + assertPublishOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserNameAndPasswordEnv(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-u"); + publishCommand.add("username"); + publishCommand.add("-pw:env"); + publishCommand.add("PASSWORD"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand, Map.of("PASSWORD", "password")); + + assertPublishOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserNamePasswordFile(final char mqttVersion) throws Exception { + final Path passwordFile = Files.createTempFile("password-file", ".txt"); + passwordFile.toFile().deleteOnExit(); + Files.writeString(passwordFile, "password"); + + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-u"); + publishCommand.add("username"); + publishCommand.add("-pw:file"); + publishCommand.add(passwordFile.toString()); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertPublishOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectWill(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-Wt"); + publishCommand.add("test-will-topic"); + publishCommand.add("-Wm"); + publishCommand.add("will-message"); + publishCommand.add("-Wq"); + publishCommand.add("2"); + publishCommand.add("-Wr"); + publishCommand.add("-We"); + publishCommand.add("120"); + publishCommand.add("-Wd"); + publishCommand.add("180"); + publishCommand.add("-Wpf"); + publishCommand.add(PayloadFormatIndicator.UTF_8.name()); + publishCommand.add("-Wct"); + publishCommand.add("content-type"); + publishCommand.add("-Wrt"); + publishCommand.add("will-response-topic"); + publishCommand.add("-Wup"); + publishCommand.add("key1=value1"); + publishCommand.add("-Wup"); + publishCommand.add("key2=value2"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Will Message Expiry was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Will Payload Format was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Will Delay Interval was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Will Content Type was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Will Response Topic was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1")); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + final WillPublishBuilder expectedWillBuilder = Builders.willPublish() + .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) + .topic("test-will-topic") + .qos(Qos.EXACTLY_ONCE) + .messageExpiryInterval(4294967295L) + .retain(true); + + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setWillPublish(expectedWillBuilder.build()); + }); + + } else { + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + final WillPublishBuilder expectedWillBuilder = Builders.willPublish() + .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) + .topic("test-will-topic") + .qos(Qos.EXACTLY_ONCE) + .retain(true) + .payloadFormatIndicator(PayloadFormatIndicator.UNSPECIFIED) + .messageExpiryInterval(120) + .willDelay(180) + .payloadFormatIndicator(PayloadFormatIndicator.UTF_8) + .contentType("content-type") + .responseTopic("will-response-topic") + .userProperty("key1", "value1") + .userProperty("key2", "value2"); + + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setWillPublish(expectedWillBuilder.build()); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectReceiveMax(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--rcvMax"); + publishCommand.add("100"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setReceiveMaximum(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectMaxPacketSize(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--maxPacketSize"); + publishCommand.add("100"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setMaximumPacketSize(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--topicAliasMax"); + publishCommand.add("100"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setTopicAliasMaximum(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectRequestProblemInformation(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--no-reqProblemInfo"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestProblemInformation(false); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectRequestResponseInformation(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--reqResponseInfo"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains( + "Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestResponseInformation(true); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectSessionExpiryInterval(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-se"); + publishCommand.add("100"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setSessionExpiryInterval(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserProperties(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-Cup"); + publishCommand.add("key1=value1"); + publishCommand.add("-Cup"); + publishCommand.add("key2=value2"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput().contains("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( + MqttUserProperty.of("key1", "value1"), + MqttUserProperty.of("key2", "value2"))); + connectAssertion.setUserProperties(expectedUserProperties); + } + }); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_topic() throws Exception { - final List publishCommand = List.of( - "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()) - ); + final List publishCommand = + List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort())); final ExecutionResult executionResult = mqttCli.execute(publishCommand); @@ -101,17 +641,50 @@ void test_publish_missing_topic() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_message() throws Exception { - final List publishCommand = List.of( - "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-t", "test" - ); + final List publishCommand = + List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test"); final ExecutionResult executionResult = mqttCli.execute(publishCommand); assertEquals(2, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m | -m:file )") - || executionResult.getErrorOutput().contains("Error: Missing required argument (specify one of these): (-m:file= | -m=)")); + assertTrue(executionResult.getErrorOutput() + .contains("Error: Missing required argument (specify one of these):")); + } + + private void assertPublishOutput(final @NotNull ExecutionResult executionResult) { + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); + assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); + assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } + + private @NotNull List defaultPublishCommand(final char mqttVersion) { + final ArrayList publishCommand = new ArrayList<>(); + publishCommand.add("pub"); + publishCommand.add("-h"); + publishCommand.add(hivemq.getHost()); + publishCommand.add("-p"); + publishCommand.add(String.valueOf(hivemq.getMqttPort())); + publishCommand.add("-V"); + publishCommand.add(String.valueOf(mqttVersion)); + publishCommand.add("-i"); + publishCommand.add("cliTest"); + publishCommand.add("-t"); + publishCommand.add("test"); + publishCommand.add("-m"); + publishCommand.add("test"); + publishCommand.add("-d"); + return publishCommand; } } \ No newline at end of file diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java index 7d8be1ee2..6baaa5d0f 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellDisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.AwaitOutput; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellExitST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellExitST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java index 430f40522..0e00a3a6c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellExitST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.shell; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellListST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellListST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java index bab5bab6e..1d0636179 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellListST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.shell; import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java index ed714cfb8..ecc61a581 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.AwaitOutput; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java index 571b344b8..05365f454 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; import com.google.gson.JsonObject; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java similarity index 97% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java index f3dc2c15c..9d3d2a593 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellSwitchST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.shell; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java index 6760357c7..08455da43 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/ShellUnsubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell; +package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.AwaitOutput; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java index 2de2c8bfe..a5e69fa1c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell.connect; +package com.hivemq.cli.commands.shell.connect; import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index a5d6039ba..9721934d4 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell.connect; +package com.hivemq.cli.commands.shell.connect; import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.AwaitOutput; @@ -172,6 +172,9 @@ void test_cleanStart(final char mqttVersion) throws Exception { assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '3') { + connectAssertion.setSessionExpiryInterval(4294967295L); + } connectAssertion.setCleanStart(false); }); } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java index 1d033156d..a6dd9b53c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell.connect; +package com.hivemq.cli.commands.shell.connect; import com.google.common.io.Resources; import com.hivemq.cli.utils.HiveMQ; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java rename to src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java index b939e274c..803af12fd 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/shell/connect/ShellConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.commands.cli.shell.connect; +package com.hivemq.cli.commands.shell.connect; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java index cf190cb87..5c6df445a 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java @@ -23,10 +23,14 @@ import com.hivemq.embedded.EmbeddedHiveMQBuilder; import com.hivemq.extension.sdk.api.ExtensionMain; import com.hivemq.extension.sdk.api.annotations.NotNull; +import com.hivemq.extension.sdk.api.interceptor.connack.ConnackOutboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.connack.parameter.ConnackOutboundInput; +import com.hivemq.extension.sdk.api.interceptor.connack.parameter.ConnackOutboundOutput; import com.hivemq.extension.sdk.api.interceptor.connect.ConnectInboundInterceptor; import com.hivemq.extension.sdk.api.interceptor.unsubscribe.UnsubscribeInboundInterceptor; import com.hivemq.extension.sdk.api.interceptor.unsubscribe.parameter.UnsubscribeInboundInput; import com.hivemq.extension.sdk.api.interceptor.unsubscribe.parameter.UnsubscribeInboundOutput; +import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; import com.hivemq.extension.sdk.api.packets.subscribe.SubscribePacket; @@ -60,6 +64,7 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCal private EmbeddedHiveMQ hivemq; private List connectPackets; + private List connackPackets; private List publishPackets; private List subscribePackets; private List unsubscribePackets; @@ -86,6 +91,7 @@ public void beforeAll(final ExtensionContext context) throws IOException { this.port = generatePort(); this.connectPackets = new ArrayList<>(); + this.connackPackets = new ArrayList<>(); this.publishPackets = new ArrayList<>(); this.disconnectInformations = new ArrayList<>(); this.subscribePackets = new ArrayList<>(); @@ -127,8 +133,14 @@ public void beforeAll(final ExtensionContext context) throws IOException { public void extensionStart( final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { + + // Add Connect & Connack Interceptors final ConnectInboundInterceptor connectInboundInterceptor = (connectInboundInput, connectInboundOutput) -> connectPackets.add(connectInboundInput.getConnectPacket()); + final ConnackOutboundInterceptor connackOutboundInterceptor = (connackOutboundInput, connackOutboundOutput) -> connackPackets.add(connackOutboundInput.getConnackPacket()); Services.interceptorRegistry().setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); + Services.interceptorRegistry().setConnackOutboundInterceptorProvider(input -> connackOutboundInterceptor); + + // Add all other interceptors Services.initializerRegistry().setClientInitializer((initializerInput, clientContext) -> { clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> { @@ -180,6 +192,7 @@ public void afterAll(final ExtensionContext context) { @Override public void afterEach(final ExtensionContext context) { connectPackets.clear(); + connackPackets.clear(); disconnectInformations.clear(); publishPackets.clear(); subscribePackets.clear(); @@ -190,6 +203,10 @@ public void afterEach(final ExtensionContext context) { return connectPackets; } + public @NotNull List getConnackPackets() { + return connackPackets; + } + public @NotNull List getPublishPackets() { return publishPackets; } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java index 0d1c7e7f2..c4c6138a2 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java @@ -19,10 +19,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -39,15 +36,18 @@ public class MqttCli { * Executes a mqtt-cli command in blocking manner. This method should be used for all mqtt-cli commands which * exit the cli with an exit code. * @param command the command to execute with the mqtt cli + * @param environmentVariables the environment variables to start the process with * @return an {@link ExecutionResult} which contains the std-output, err-ouput and exit-code of the command's execution * @throws IOException when an error occurred while starting the process or reading its output * @throws InterruptedException when the process was interrupted */ - public @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { + public @NotNull ExecutionResult execute(final @NotNull List command, final @NotNull Map environmentVariables) throws IOException, InterruptedException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); - final Process process = new ProcessBuilder(fullCommand).start(); + final ProcessBuilder processBuilder = new ProcessBuilder(fullCommand); + processBuilder.environment().putAll(environmentVariables); + final Process process = processBuilder.start(); final int exitCode = process.waitFor(); @@ -63,22 +63,50 @@ public class MqttCli { return new ExecutionResult(exitCode, stdOut, stdErr); } + /** + * Executes a mqtt-cli command in blocking manner. This method should be used for all mqtt-cli commands which + * exit the cli with an exit code. + * @param command the command to execute with the mqtt cli + * @return an {@link ExecutionResult} which contains the std-output, err-ouput and exit-code of the command's execution + * @throws IOException when an error occurred while starting the process or reading its output + * @throws InterruptedException when the process was interrupted + */ + public @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { + return execute(command, Map.of()); + } + /** * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not * exit the process like the subscribe command. * @param command the command to execute with the mqtt cli + * @param environmentVariables the environment variables to start the process with * @return an {@link AwaitOutput} which can be used to wait for std-out std-err messages * @throws IOException when an error occurred while starting the process */ - public @NotNull AwaitOutput executeAsync(final @NotNull List command) + public @NotNull AwaitOutput executeAsync(final @NotNull List command, final @NotNull Map environmentVariables) throws IOException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); - final Process process = new ProcessBuilder(fullCommand).start(); + final ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.environment().putAll(environmentVariables); + final Process process = processBuilder.start(); + final ProcessIO processIO = ProcessIO.startReading(process); return new AwaitOutput(processIO, null, String.join(" ", command)); } + /** + * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not + * exit the process like the subscribe command. + * @param command the command to execute with the mqtt cli + * @return an {@link AwaitOutput} which can be used to wait for std-out std-err messages + * @throws IOException when an error occurred while starting the process + */ + public @NotNull AwaitOutput executeAsync(final @NotNull List command) + throws IOException { + return executeAsync(command, Map.of()); + } + } From 5d7dabfcb116b76511734e63e1bce4c8688d5596 Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 11 Oct 2022 15:53:35 +0200 Subject: [PATCH 48/64] System Tests > Add publish tests --- .../PublishConnectST.java} | 61 +-- .../cli/commands/cli/publish/PublishST.java | 419 ++++++++++++++++++ .../cli/{ => subscribe}/SubscribeST.java | 2 +- 3 files changed, 423 insertions(+), 59 deletions(-) rename src/systemTest/java/com/hivemq/cli/commands/cli/{PublishST.java => publish/PublishConnectST.java} (91%) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java rename src/systemTest/java/com/hivemq/cli/commands/cli/{ => subscribe}/SubscribeST.java (98%) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java similarity index 91% rename from src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index eea9bfe92..b0b8684a8 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -13,15 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.hivemq.cli.commands.cli; +package com.hivemq.cli.commands.cli.publish; import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.ExecutionResult; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; -import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; -import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.general.MqttVersion; @@ -31,58 +28,31 @@ import com.hivemq.extension.sdk.api.services.builder.WillPublishBuilder; import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.testcontainers.utility.DockerImageName; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; import static org.junit.jupiter.api.Assertions.*; -public class PublishST { +public class PublishConnectST { @RegisterExtension private static final HiveMQ hivemq = HiveMQ.builder().build(); private final @NotNull MqttCli mqttCli = new MqttCli(); - @ParameterizedTest - @Timeout(value = 3, unit = TimeUnit.MINUTES) - @ValueSource(chars = {'3', '5'}) - void test_successfulConnectAndPublish(final char mqttVersion) throws Exception { - final List publishCommand = defaultPublishCommand(mqttVersion); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); - - assertPublishOutput(executionResult); - - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); - }); - - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { - publishAssertion.setTopic("test"); - publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); - }); - } - @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -626,31 +596,6 @@ void test_connectUserProperties(final char mqttVersion) throws Exception { }); } - @Test - @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_topic() throws Exception { - final List publishCommand = - List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort())); - - final ExecutionResult executionResult = mqttCli.execute(publishCommand); - - assertEquals(2, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); - } - - @Test - @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_publish_missing_message() throws Exception { - final List publishCommand = - List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test"); - - final ExecutionResult executionResult = mqttCli.execute(publishCommand); - - assertEquals(2, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput() - .contains("Error: Missing required argument (specify one of these):")); - } - private void assertPublishOutput(final @NotNull ExecutionResult executionResult) { assertEquals(0, executionResult.getExitCode()); assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); @@ -687,4 +632,4 @@ private void assertPublishOutput(final @NotNull ExecutionResult executionResult) publishCommand.add("-d"); return publishCommand; } -} \ No newline at end of file +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java new file mode 100644 index 000000000..38901ca58 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java @@ -0,0 +1,419 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.publish; + +import com.google.common.collect.ImmutableList; +import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCli; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; +import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static org.junit.jupiter.api.Assertions.*; + +public class PublishST { + + @RegisterExtension + private static final HiveMQ hivemq = HiveMQ.builder().build(); + + private final @NotNull MqttCli mqttCli = new MqttCli(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_successfulConnectAndPublish(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertPublishOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_retain(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-r"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setRetain(true); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_messageFromFile(final char mqttVersion) throws Exception { + final Path messageFile = Files.createTempFile("message-file", ".txt"); + Files.writeString(messageFile, "message-from-file"); + + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.remove("-m"); + publishCommand.remove("message"); + publishCommand.add("-m:file"); + publishCommand.add(messageFile.toString()); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message-from-file".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_emptyMessage(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.remove("-m"); + publishCommand.remove("message"); + publishCommand.add("-m:empty"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.allocate(0)); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopics(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.remove("-t"); + publishCommand.remove("test"); + publishCommand.add("-t"); + publishCommand.add("test1"); + publishCommand.add("-t"); + publishCommand.add("test2"); + publishCommand.add("-t"); + publishCommand.add("test3"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test1")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test2")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test3")); + + final Set topicSet = hivemq.getPublishPackets() + .stream() + .map(PublishPacket::getTopic) + .collect(Collectors.toSet()); + assertTrue(topicSet.containsAll(List.of("test1", "test2", "test3"))); + + for (final PublishPacket publishPacket : hivemq.getPublishPackets()) { + assertPublishPacket(publishPacket, publishAssertion -> { + publishAssertion.setTopic(publishPacket.getTopic()); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.remove("-t"); + publishCommand.remove("test"); + publishCommand.add("-t"); + publishCommand.add("test1"); + publishCommand.add("-t"); + publishCommand.add("test2"); + publishCommand.add("-t"); + publishCommand.add("test3"); + publishCommand.add("-q"); + publishCommand.add("0"); + publishCommand.add("-q"); + publishCommand.add("1"); + publishCommand.add("-q"); + publishCommand.add("2"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test1, payload=7byte, qos=AT_MOST_ONCE, retain=false}")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test2, payload=7byte, qos=AT_LEAST_ONCE, retain=false}")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test3, payload=7byte, qos=EXACTLY_ONCE, retain=false}")); + + final Map topicToPublishPacket = hivemq.getPublishPackets() + .stream() + .collect(Collectors.toMap(PublishPacket::getTopic, publishPacket -> publishPacket)); + + assertEquals(Qos.AT_MOST_ONCE, topicToPublishPacket.get("test1").getQos()); + assertEquals(Qos.AT_LEAST_ONCE, topicToPublishPacket.get("test2").getQos()); + assertEquals(Qos.EXACTLY_ONCE, topicToPublishPacket.get("test3").getQos()); + + for (final PublishPacket publishPacket : hivemq.getPublishPackets()) { + assertPublishPacket(publishPacket, publishAssertion -> { + publishAssertion.setTopic(publishPacket.getTopic()); + publishAssertion.setQos(publishPacket.getQos()); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_messageExpiryInterval(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-e"); + publishCommand.add("60"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput().contains("Publish message expiry was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + if (mqttVersion == '5') { + publishAssertion.setMessageExpiryInterval(60); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_payloadFormatIndicator(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-pf"); + publishCommand.add("utf8"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput().contains("Publish payload format indicator was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + if (mqttVersion == '5') { + publishAssertion.setPayloadFormatIndicator(PayloadFormatIndicator.UTF_8); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_contentType(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-ct"); + publishCommand.add("content-type"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput().contains("Publish content type was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + if (mqttVersion == '5') { + publishAssertion.setContentType("content-type"); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_responseTopic(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-rt"); + publishCommand.add("response-topic"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput().contains("Publish response topic was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + if (mqttVersion == '5') { + publishAssertion.setResponseTopic("response-topic"); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_correlationData(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-cd"); + publishCommand.add("correlation-data"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput().contains("Publish correlation data was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + if (mqttVersion == '5') { + publishAssertion.setCorrelationData(ByteBuffer.wrap("correlation-data".getBytes(StandardCharsets.UTF_8))); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userProperties(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("-up"); + publishCommand.add("key1=value1"); + publishCommand.add("-up"); + publishCommand.add("key2=value2"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput().contains("Publish user properties were set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + if (mqttVersion == '5') { + final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( + MqttUserProperty.of("key1", "value1"), + MqttUserProperty.of("key2", "value2"))); + publishAssertion.setUserProperties(expectedUserProperties); + } + }); + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_publish_missing_topic() throws Exception { + final List publishCommand = + List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort())); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_publish_missing_message() throws Exception { + final List publishCommand = + List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test"); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + + assertEquals(2, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput() + .contains("Error: Missing required argument (specify one of these):")); + } + + private void assertPublishOutput(final @NotNull ExecutionResult executionResult) { + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); + assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); + assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH")); + assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } + + private @NotNull List defaultPublishCommand(final char mqttVersion) { + final ArrayList publishCommand = new ArrayList<>(); + publishCommand.add("pub"); + publishCommand.add("-h"); + publishCommand.add(hivemq.getHost()); + publishCommand.add("-p"); + publishCommand.add(String.valueOf(hivemq.getMqttPort())); + publishCommand.add("-V"); + publishCommand.add(String.valueOf(mqttVersion)); + publishCommand.add("-i"); + publishCommand.add("cliTest"); + publishCommand.add("-t"); + publishCommand.add("test"); + publishCommand.add("-m"); + publishCommand.add("message"); + publishCommand.add("-d"); + return publishCommand; + } +} \ No newline at end of file diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java rename to src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java index 60cd4052b..90dbe407b 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.commands.cli; +package com.hivemq.cli.commands.cli.subscribe; import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.ExecutionResult; From 8ff75f35cd91c43306307d775b24d4392b380deb Mon Sep 17 00:00:00 2001 From: gitseti Date: Tue, 11 Oct 2022 17:20:58 +0200 Subject: [PATCH 49/64] System Tests > Fix executeAsync() --- src/systemTest/java/com/hivemq/cli/utils/MqttCli.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java index c4c6138a2..15b7ea4fa 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java @@ -88,7 +88,7 @@ public class MqttCli { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); - final ProcessBuilder processBuilder = new ProcessBuilder(command); + final ProcessBuilder processBuilder = new ProcessBuilder(fullCommand); processBuilder.environment().putAll(environmentVariables); final Process process = processBuilder.start(); From 84553f4fd3dc8184b1631a2362230ac2bc07ce75 Mon Sep 17 00:00:00 2001 From: gitseti Date: Wed, 12 Oct 2022 09:57:35 +0200 Subject: [PATCH 50/64] System Tests > Add publish tls and websockets tests --- .../cli/publish/PublishConnectTlsST.java | 93 +++++++++++++++++++ .../publish/PublishConnectWebsocketsST.java | 88 ++++++++++++++++++ .../commands/cli/subscribe/SubscribeST.java | 11 +-- .../cli/utils/ExecutionResultAsync.java | 39 ++++++++ .../java/com/hivemq/cli/utils/MqttCli.java | 11 ++- 5 files changed, 230 insertions(+), 12 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java new file mode 100644 index 000000000..cc9e226b5 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java @@ -0,0 +1,93 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.publish; + +import com.google.common.io.Resources; +import com.hivemq.cli.utils.*; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static org.junit.jupiter.api.Assertions.fail; + +public class PublishConnectTlsST { + @RegisterExtension + private final static HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); + + final MqttCli mqttCli = new MqttCli(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_mutualTls(final char mqttVersion) throws Exception { + + final String clientKeyPem = Resources.getResource("tls/client-key.pem").getPath(); + final String clientCertPem = Resources.getResource("tls/client-cert.pem").getPath(); + final String serverPem = Resources.getResource("tls/server.pem").getPath(); + + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getMqttTlsPort()), + "-V", String.valueOf(mqttVersion), + "-i", "cliTest", + "-t", "test", + "-m", "message", + "--cafile", + serverPem, + "--key", + clientKeyPem, + "--cert", + clientCertPem, + "-d" + ); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(publishCommand); + executionResult.getAwaitOutput().awaitStdOut("Enter private key password:"); + executionResult.write("changeme"); + executionResult.getAwaitOutput().awaitStdOut("received PUBLISH acknowledgement"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("test"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + }); + + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java new file mode 100644 index 000000000..2dabe9a1f --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.publish; + +import com.hivemq.cli.utils.ExecutionResult; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCli; +import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static org.junit.jupiter.api.Assertions.*; + +public class PublishConnectWebsocketsST { + + @RegisterExtension + private final static HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); + + final MqttCli mqttCli = new MqttCli(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_websockets(final char mqttVersion) throws Exception { + final List publishCommand = List.of( + "pub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getWebsocketsPort()), + "-V", String.valueOf(mqttVersion), + "-i", "cliTest", + "-t", "topic", + "-m", "message", + "-ws", + "-ws:path", + hivemq.getWebsocketsPath(), + "-d" + ); + + final ExecutionResult executionResult = mqttCli.execute(publishCommand); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); + assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + publishAssertion.setTopic("topic"); + publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); + }); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } +} + diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java index 90dbe407b..415b0f53d 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java @@ -22,14 +22,10 @@ import com.hivemq.cli.utils.MqttCli; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; -import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; -import org.testcontainers.utility.DockerImageName; import java.nio.charset.StandardCharsets; import java.util.List; @@ -47,7 +43,7 @@ public class SubscribeST { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successful_subscribe() throws Exception { + void test_successfulConnectAndSubscribe() throws Exception { final List subscribeCommand = List.of( "sub", "-h", hivemq.getHost(), @@ -63,8 +59,8 @@ void test_successful_subscribe() throws Exception { .buildBlocking(); publisher.connect(); - final AwaitOutput awaitOutput = mqttCli.executeAsync(subscribeCommand); - + final AwaitOutput awaitOutput = mqttCli.executeAsync(subscribeCommand).getAwaitOutput(); + awaitOutput.awaitStdOut("sending SUBSCRIBE"); awaitOutput.awaitStdOut("received SUBACK"); publisher.publishWith().topic("test").payload("testReturn".getBytes(StandardCharsets.UTF_8)).send(); @@ -86,4 +82,5 @@ void test_subscribe_missing_topic() throws Exception { assertEquals(2, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); } + } diff --git a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java new file mode 100644 index 000000000..a88b2336b --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public class ExecutionResultAsync { + + private final @NotNull AwaitOutput awaitOutput; + private final @NotNull ProcessIO processIO; + + public ExecutionResultAsync(final @NotNull AwaitOutput awaitOutput, final @NotNull ProcessIO processIO) { + this.awaitOutput = awaitOutput; + this.processIO = processIO; + } + + public @NotNull AwaitOutput getAwaitOutput() { + return awaitOutput; + } + + public void write(final @NotNull String output) throws IOException { + processIO.writeMsg(output); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java index 15b7ea4fa..d4d71e466 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java @@ -80,10 +80,10 @@ public class MqttCli { * exit the process like the subscribe command. * @param command the command to execute with the mqtt cli * @param environmentVariables the environment variables to start the process with - * @return an {@link AwaitOutput} which can be used to wait for std-out std-err messages + * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages * @throws IOException when an error occurred while starting the process */ - public @NotNull AwaitOutput executeAsync(final @NotNull List command, final @NotNull Map environmentVariables) + public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command, final @NotNull Map environmentVariables) throws IOException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); @@ -94,17 +94,18 @@ public class MqttCli { final ProcessIO processIO = ProcessIO.startReading(process); - return new AwaitOutput(processIO, null, String.join(" ", command)); + final AwaitOutput awaitOutput = new AwaitOutput(processIO, null, String.join(" ", command)); + return new ExecutionResultAsync(awaitOutput, processIO); } /** * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not * exit the process like the subscribe command. * @param command the command to execute with the mqtt cli - * @return an {@link AwaitOutput} which can be used to wait for std-out std-err messages + * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages * @throws IOException when an error occurred while starting the process */ - public @NotNull AwaitOutput executeAsync(final @NotNull List command) + public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command) throws IOException { return executeAsync(command, Map.of()); } From 708de6df7a7796fe91c74716fc56c528dd41e7c1 Mon Sep 17 00:00:00 2001 From: gitseti Date: Wed, 12 Oct 2022 13:01:55 +0200 Subject: [PATCH 51/64] System Tests > Add subscribe command tests --- .../cli/publish/PublishConnectST.java | 46 ++- .../cli/publish/PublishConnectTlsST.java | 8 +- .../publish/PublishConnectWebsocketsST.java | 4 +- .../cli/commands/cli/publish/PublishST.java | 32 +- .../commands/cli/subscribe/SubscribeST.java | 367 ++++++++++++++++-- .../cli/commands/shell/ShellPublishST.java | 2 +- .../cli/utils/ExecutionResultAsync.java | 25 +- .../java/com/hivemq/cli/utils/MqttCli.java | 39 +- .../com/hivemq/cli/utils/MqttCliAsync.java | 79 ++++ .../com/hivemq/cli/utils/MqttCliShell.java | 23 +- .../cli/utils/OrphanProcessCleanup.java | 46 +++ .../utils/assertions/SubscribeAssertion.java | 1 + 12 files changed, 532 insertions(+), 140 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/MqttCliAsync.java create mode 100644 src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index b0b8684a8..6675246c3 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -50,9 +50,7 @@ public class PublishConnectST { @RegisterExtension private static final HiveMQ hivemq = HiveMQ.builder().build(); - - private final @NotNull MqttCli mqttCli = new MqttCli(); - + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -70,7 +68,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { "test", "-d"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(0, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput() .contains("wrong-host: nodename nor servname provided, or not known")); @@ -93,7 +91,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { "test", "-d"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(0, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput().contains("Connection refused")); } @@ -105,7 +103,7 @@ void test_connectCleanStart(final char mqttVersion) throws Exception { final List publishCommand = defaultPublishCommand(mqttVersion); publishCommand.add("--no-cleanStart"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { @@ -125,7 +123,7 @@ void test_connectKeepAlive(final char mqttVersion) throws Exception { publishCommand.add("-k"); publishCommand.add("100"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { @@ -142,7 +140,7 @@ void test_connectNoClientId(final char mqttVersion) throws Exception { publishCommand.remove("-i"); publishCommand.remove("cliTest"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertTrue(executionResult.getStandardOutput().contains("sending CONNECT")); assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); @@ -178,7 +176,7 @@ void test_connectIdentifierPrefix(final char mqttVersion) throws Exception { publishCommand.add("-ip"); publishCommand.add("test-"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); @@ -200,7 +198,7 @@ void test_connectUserName(final char mqttVersion) throws Exception { publishCommand.add("-u"); publishCommand.add("username"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { @@ -217,7 +215,7 @@ void test_connectPassword(final char mqttVersion) throws Exception { publishCommand.add("-pw"); publishCommand.add("password"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); if (mqttVersion == '3') { assertTrue(executionResult.getErrorOutput() @@ -240,7 +238,7 @@ void test_connectPasswordEnv(final char mqttVersion) throws Exception { publishCommand.add("-pw:env"); publishCommand.add("PASSWORD"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand, Map.of("PASSWORD", "password")); + final ExecutionResult executionResult = MqttCli.execute(publishCommand, Map.of("PASSWORD", "password")); if (mqttVersion == '3') { assertTrue(executionResult.getErrorOutput() @@ -267,7 +265,7 @@ void test_connectPasswordFile(final char mqttVersion) throws Exception { publishCommand.add("-pw:file"); publishCommand.add(passwordFile.toString()); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); if (mqttVersion == '3') { assertTrue(executionResult.getErrorOutput() @@ -292,7 +290,7 @@ void test_connectUserNameAndPassword(final char mqttVersion) throws Exception { publishCommand.add("-pw"); publishCommand.add("password"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertPublishOutput(executionResult); @@ -313,7 +311,7 @@ void test_connectUserNameAndPasswordEnv(final char mqttVersion) throws Exception publishCommand.add("-pw:env"); publishCommand.add("PASSWORD"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand, Map.of("PASSWORD", "password")); + final ExecutionResult executionResult = MqttCli.execute(publishCommand, Map.of("PASSWORD", "password")); assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { @@ -337,7 +335,7 @@ void test_connectUserNamePasswordFile(final char mqttVersion) throws Exception { publishCommand.add("-pw:file"); publishCommand.add(passwordFile.toString()); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { @@ -374,7 +372,7 @@ void test_connectWill(final char mqttVersion) throws Exception { publishCommand.add("-Wup"); publishCommand.add("key2=value2"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -433,7 +431,7 @@ void test_connectReceiveMax(final char mqttVersion) throws Exception { publishCommand.add("--rcvMax"); publishCommand.add("100"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -457,7 +455,7 @@ void test_connectMaxPacketSize(final char mqttVersion) throws Exception { publishCommand.add("--maxPacketSize"); publishCommand.add("100"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -481,7 +479,7 @@ void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { publishCommand.add("--topicAliasMax"); publishCommand.add("100"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -504,7 +502,7 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except final List publishCommand = defaultPublishCommand(mqttVersion); publishCommand.add("--no-reqProblemInfo"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -527,7 +525,7 @@ void test_connectRequestResponseInformation(final char mqttVersion) throws Excep final List publishCommand = defaultPublishCommand(mqttVersion); publishCommand.add("--reqResponseInfo"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -552,7 +550,7 @@ void test_connectSessionExpiryInterval(final char mqttVersion) throws Exception publishCommand.add("-se"); publishCommand.add("100"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -578,7 +576,7 @@ void test_connectUserProperties(final char mqttVersion) throws Exception { publishCommand.add("-Cup"); publishCommand.add("key2=value2"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java index cc9e226b5..d156a1075 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java @@ -34,10 +34,12 @@ import static org.junit.jupiter.api.Assertions.fail; public class PublishConnectTlsST { + @RegisterExtension private final static HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); - final MqttCli mqttCli = new MqttCli(); + @RegisterExtension + private final static MqttCliAsync mqttCli = new MqttCliAsync(); @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @@ -66,9 +68,9 @@ void test_mutualTls(final char mqttVersion) throws Exception { ); final ExecutionResultAsync executionResult = mqttCli.executeAsync(publishCommand); - executionResult.getAwaitOutput().awaitStdOut("Enter private key password:"); + executionResult.awaitStdOut("Enter private key password:"); executionResult.write("changeme"); - executionResult.getAwaitOutput().awaitStdOut("received PUBLISH acknowledgement"); + executionResult.awaitStdOut("received PUBLISH acknowledgement"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java index 2dabe9a1f..54c53c9de 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java @@ -40,8 +40,6 @@ public class PublishConnectWebsocketsST { @RegisterExtension private final static HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); - final MqttCli mqttCli = new MqttCli(); - @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -60,7 +58,7 @@ void test_websockets(final char mqttVersion) throws Exception { "-d" ); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(0, executionResult.getExitCode()); assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java index 38901ca58..5462663a3 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java @@ -52,15 +52,13 @@ public class PublishST { @RegisterExtension private static final HiveMQ hivemq = HiveMQ.builder().build(); - - private final @NotNull MqttCli mqttCli = new MqttCli(); - + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_successfulConnectAndPublish(final char mqttVersion) throws Exception { final List publishCommand = defaultPublishCommand(mqttVersion); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); @@ -81,7 +79,7 @@ void test_retain(final char mqttVersion) throws Exception { final List publishCommand = defaultPublishCommand(mqttVersion); publishCommand.add("-r"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { @@ -104,7 +102,7 @@ void test_messageFromFile(final char mqttVersion) throws Exception { publishCommand.add("-m:file"); publishCommand.add(messageFile.toString()); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { @@ -122,7 +120,7 @@ void test_emptyMessage(final char mqttVersion) throws Exception { publishCommand.remove("message"); publishCommand.add("-m:empty"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { @@ -145,7 +143,7 @@ void test_multipleTopics(final char mqttVersion) throws Exception { publishCommand.add("-t"); publishCommand.add("test3"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test1")); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test2")); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test3")); @@ -184,7 +182,7 @@ void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { publishCommand.add("-q"); publishCommand.add("2"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test1, payload=7byte, qos=AT_MOST_ONCE, retain=false}")); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test2, payload=7byte, qos=AT_LEAST_ONCE, retain=false}")); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test3, payload=7byte, qos=EXACTLY_ONCE, retain=false}")); @@ -214,7 +212,7 @@ void test_messageExpiryInterval(final char mqttVersion) throws Exception { publishCommand.add("-e"); publishCommand.add("60"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -238,7 +236,7 @@ void test_payloadFormatIndicator(final char mqttVersion) throws Exception { publishCommand.add("-pf"); publishCommand.add("utf8"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -262,7 +260,7 @@ void test_contentType(final char mqttVersion) throws Exception { publishCommand.add("-ct"); publishCommand.add("content-type"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -286,7 +284,7 @@ void test_responseTopic(final char mqttVersion) throws Exception { publishCommand.add("-rt"); publishCommand.add("response-topic"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -310,7 +308,7 @@ void test_correlationData(final char mqttVersion) throws Exception { publishCommand.add("-cd"); publishCommand.add("correlation-data"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -336,7 +334,7 @@ void test_userProperties(final char mqttVersion) throws Exception { publishCommand.add("-up"); publishCommand.add("key2=value2"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); if (mqttVersion == '3') { @@ -361,7 +359,7 @@ void test_publish_missing_topic() throws Exception { final List publishCommand = List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort())); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(2, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); @@ -373,7 +371,7 @@ void test_publish_missing_message() throws Exception { final List publishCommand = List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test"); - final ExecutionResult executionResult = mqttCli.execute(publishCommand); + final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(2, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput() diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java index 415b0f53d..bc633d124 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java @@ -16,71 +16,380 @@ package com.hivemq.cli.commands.cli.subscribe; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.ExecutionResult; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCli; +import com.google.common.collect.ImmutableList; +import com.google.gson.JsonObject; +import com.hivemq.cli.utils.*; +import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; +import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.extensions.packets.subscribe.SubscriptionImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Base64; import java.util.List; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; +import static org.junit.jupiter.api.Assertions.*; public class SubscribeST { @RegisterExtension private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); - private final @NotNull MqttCli mqttCli = new MqttCli(); + @RegisterExtension + private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); - @Test + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_successfulConnectAndSubscribe() throws Exception { - final List subscribeCommand = List.of( - "sub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-t", "test", - "-d" - ); + @ValueSource(chars = {'3', '5'}) + void test_successfulConnectAndSubscribe(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); - final Mqtt5BlockingClient publisher = Mqtt5Client.builder() - .identifier("publisher") - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) - .buildBlocking(); - publisher.connect(); + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribe(executionResult); + + publishMessage("topic", "message"); + + executionResult.awaitStdOut("message"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_qos(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-q"); + subscribeCommand.add("1"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribe(executionResult); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic", Qos.AT_LEAST_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopics(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.remove("-t"); + subscribeCommand.remove("topic"); + subscribeCommand.add("-t"); + subscribeCommand.add("topic1"); + subscribeCommand.add("-t"); + subscribeCommand.add("topic2"); + subscribeCommand.add("-t"); + subscribeCommand.add("topic3"); - final AwaitOutput awaitOutput = mqttCli.executeAsync(subscribeCommand).getAwaitOutput(); - awaitOutput.awaitStdOut("sending SUBSCRIBE"); - awaitOutput.awaitStdOut("received SUBACK"); + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand) + .awaitStdOut("received SUBACK") + .awaitStdOut("received SUBACK") + .awaitStdOut("received SUBACK"); - publisher.publishWith().topic("test").payload("testReturn".getBytes(StandardCharsets.UTF_8)).send(); + publishMessage("topic1", "message1"); + executionResult.awaitStdOut("message1"); - awaitOutput.awaitStdOut("testReturn"); + publishMessage("topic2", "message2"); + executionResult.awaitStdOut("message2"); + + publishMessage("topic3", "message3"); + executionResult.awaitStdOut("message3"); + + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic1", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic2", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic3", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_multipleTopicsMultipleQoS(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.remove("-t"); + subscribeCommand.remove("topic"); + subscribeCommand.add("-t"); + subscribeCommand.add("topic1"); + subscribeCommand.add("-t"); + subscribeCommand.add("topic2"); + subscribeCommand.add("-t"); + subscribeCommand.add("topic3"); + subscribeCommand.add("-q"); + subscribeCommand.add("0"); + subscribeCommand.add("-q"); + subscribeCommand.add("1"); + subscribeCommand.add("-q"); + subscribeCommand.add("2"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand) + .awaitStdOut("received SUBACK") + .awaitStdOut("received SUBACK") + .awaitStdOut("received SUBACK"); + + publishMessage("topic1", "message1"); + executionResult.awaitStdOut("message1"); + + publishMessage("topic2", "message2"); + executionResult.awaitStdOut("message2"); + + publishMessage("topic3", "message3"); + executionResult.awaitStdOut("message3"); + + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic1", Qos.AT_MOST_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic2", Qos.AT_LEAST_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic3", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_outputFile(final char mqttVersion) throws Exception { + final Path messageFile = Files.createTempFile("message-file", ".txt"); + messageFile.toFile().deleteOnExit(); + + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-of"); + subscribeCommand.add(messageFile.toString()); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribe(executionResult); + + publishMessage("topic", "message"); + + executionResult.awaitStdOut("message"); + + final List readLines = Files.readAllLines(messageFile); + assertEquals(1, readLines.size()); + assertEquals("message", readLines.get(0)); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_userProperties(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-up"); + subscribeCommand.add("key1=value1"); + subscribeCommand.add("-up"); + subscribeCommand.add("key2=value2"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribe(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Subscribe user properties were set but are unused in MQTT version MQTT_3_1_1"); + } + + publishMessage("topic", "message"); + + executionResult.awaitStdOut("message"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); + + subscribeAssertion.setSubscriptions(expectedSubscriptions); + if (mqttVersion == '5') { + final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( + MqttUserProperty.of("key1", "value1"), + MqttUserProperty.of("key2", "value2"))); + subscribeAssertion.setUserProperties(expectedUserProperties); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_base64(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-b64"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribe(executionResult); + + publishMessage("topic", "message"); + + final String encodedPayload = Base64.getEncoder().encodeToString("message".getBytes(StandardCharsets.UTF_8)); + executionResult.awaitStdOut(encodedPayload); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_showTopic(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-T"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribe(executionResult); + + publishMessage("topic", "message"); + + executionResult.awaitStdOut("topic: message"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_showJson(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-J"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribe(executionResult); + + final JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("property1", "value1"); + jsonObject.addProperty("property2", "value2"); + jsonObject.addProperty("property3", "value3"); + publishMessage("topic", jsonObject.toString()); + + executionResult.awaitStdOut("{\n" + + " \"topic\": \"topic\",\n" + + " \"payload\": {\n" + + " \"property1\": \"value1\",\n" + + " \"property2\": \"value2\",\n" + + " \"property3\": \"value3\"\n" + + " },\n"); + executionResult.awaitStdOut("\"qos\": \"EXACTLY_ONCE\","); + executionResult.awaitStdOut("\"receivedAt\":"); + executionResult.awaitStdOut("\"retain\": false"); + executionResult.awaitStdOut("}"); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) - void test_subscribe_missing_topic() throws Exception { + void test_subscribeMissingTopic() throws Exception { final List subscribeCommand = List.of( "sub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()) ); - final ExecutionResult executionResult = mqttCli.execute(subscribeCommand); + final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(2, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); } + private @NotNull List defaultSubscribeCommand(final char mqttVersion) { + final ArrayList subscribeCommand = new ArrayList<>(); + subscribeCommand.add("sub"); + subscribeCommand.add("-h"); + subscribeCommand.add(hivemq.getHost()); + subscribeCommand.add("-p"); + subscribeCommand.add(String.valueOf(hivemq.getMqttPort())); + subscribeCommand.add("-V"); + subscribeCommand.add(String.valueOf(mqttVersion)); + subscribeCommand.add("-i"); + subscribeCommand.add("cliTest"); + subscribeCommand.add("-t"); + subscribeCommand.add("topic"); + subscribeCommand.add("-d"); + return subscribeCommand; + } + + private void assertSubscribe(final @NotNull ExecutionResultAsync executionResultAsync) { + executionResultAsync.awaitStdOut("sending CONNECT"); + executionResultAsync.awaitStdOut("received CONNACK"); + executionResultAsync.awaitStdOut("sending SUBSCRIBE"); + executionResultAsync.awaitStdOut("received SUBACK"); + } + + private void publishMessage(final @NotNull String topic, final @NotNull String message) { + final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + .identifier("publisher") + .serverHost(hivemq.getHost()) + .serverPort(hivemq.getMqttPort()) + .buildBlocking(); + publisher.connect(); + publisher.publishWith().topic(topic).payload(message.getBytes(StandardCharsets.UTF_8)).qos(MqttQos.EXACTLY_ONCE).send(); + } + + private Subscription createSubscription(final @NotNull String topic, final @NotNull Qos qos) { + return new SubscriptionImpl(topic, qos, RetainHandling.SEND, false, false); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } + } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java index ecc61a581..88ae4274e 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java @@ -71,7 +71,7 @@ void test_successfulPublish(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_messageFromFile(final char mqttVersion) throws Exception { + void test_messageToFile(final char mqttVersion) throws Exception { final Path publishFile = Files.createTempFile("publish", "txt"); Files.write(publishFile, "message".getBytes(StandardCharsets.UTF_8)); final List publishCommand = List.of("pub", "-t", "test", "-m:file", publishFile.toString()); diff --git a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java index a88b2336b..770c656ca 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java +++ b/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java @@ -16,21 +16,36 @@ package com.hivemq.cli.utils; import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; import java.io.IOException; public class ExecutionResultAsync { - private final @NotNull AwaitOutput awaitOutput; private final @NotNull ProcessIO processIO; + private final @NotNull String command; - public ExecutionResultAsync(final @NotNull AwaitOutput awaitOutput, final @NotNull ProcessIO processIO) { - this.awaitOutput = awaitOutput; + public ExecutionResultAsync(final @NotNull ProcessIO processIO, final @NotNull String command) { this.processIO = processIO; + this.command = command; } - public @NotNull AwaitOutput getAwaitOutput() { - return awaitOutput; + public @NotNull ExecutionResultAsync awaitStdOut(final @NotNull String expectedOutput) { + try { + processIO.awaitStdOut(expectedOutput); + } catch (final TimeoutException timeoutException) { + Assertions.fail(String.format("Command '%s' did not return expected standard output '%s' in time. Actual read standard output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + } + return this; + } + + public @NotNull ExecutionResultAsync awaitStdErr(final @NotNull String expectedOutput) { + try { + processIO.awaitStdErr(expectedOutput); + } catch (final TimeoutException timeoutException) { + Assertions.fail(String.format("Command '%s' did not return expected error output '%s' in time. Actual read error output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + } + return this; } public void write(final @NotNull String output) throws IOException { diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java index d4d71e466..b6953d039 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java @@ -41,7 +41,7 @@ public class MqttCli { * @throws IOException when an error occurred while starting the process or reading its output * @throws InterruptedException when the process was interrupted */ - public @NotNull ExecutionResult execute(final @NotNull List command, final @NotNull Map environmentVariables) throws IOException, InterruptedException { + public static @NotNull ExecutionResult execute(final @NotNull List command, final @NotNull Map environmentVariables) throws IOException, InterruptedException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); @@ -71,43 +71,8 @@ public class MqttCli { * @throws IOException when an error occurred while starting the process or reading its output * @throws InterruptedException when the process was interrupted */ - public @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { + public static @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { return execute(command, Map.of()); } - /** - * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not - * exit the process like the subscribe command. - * @param command the command to execute with the mqtt cli - * @param environmentVariables the environment variables to start the process with - * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages - * @throws IOException when an error occurred while starting the process - */ - public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command, final @NotNull Map environmentVariables) - throws IOException { - final List fullCommand = new ArrayList<>(CLI_EXEC); - assertTrue(fullCommand.addAll(command)); - - final ProcessBuilder processBuilder = new ProcessBuilder(fullCommand); - processBuilder.environment().putAll(environmentVariables); - final Process process = processBuilder.start(); - - final ProcessIO processIO = ProcessIO.startReading(process); - - final AwaitOutput awaitOutput = new AwaitOutput(processIO, null, String.join(" ", command)); - return new ExecutionResultAsync(awaitOutput, processIO); - } - - /** - * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not - * exit the process like the subscribe command. - * @param command the command to execute with the mqtt cli - * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages - * @throws IOException when an error occurred while starting the process - */ - public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command) - throws IOException { - return executeAsync(command, Map.of()); - } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliAsync.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliAsync.java new file mode 100644 index 000000000..fb5d5d38e --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliAsync.java @@ -0,0 +1,79 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MqttCliAsync implements AfterEachCallback { + + final List startedProcesses = new ArrayList<>(); + + @Override + public void afterEach(final @NotNull ExtensionContext context) { + for (final Process startedProcess : startedProcesses) { + startedProcess.destroyForcibly(); + } + } + + /** + * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not + * exit the process like the subscribe command. + * @param command the command to execute with the mqtt cli + * @param environmentVariables the environment variables to start the process with + * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages + * @throws IOException when an error occurred while starting the process + */ + public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command, final @NotNull Map environmentVariables) + throws IOException { + final List fullCommand = new ArrayList<>(CLI_EXEC); + assertTrue(fullCommand.addAll(command)); + + final ProcessBuilder processBuilder = new ProcessBuilder(fullCommand); + processBuilder.environment().putAll(environmentVariables); + final Process process = processBuilder.start(); + + final ProcessIO processIO = ProcessIO.startReading(process); + final Process cliProcess = OrphanProcessCleanup.startOrphanCleanupProcess(process); + + startedProcesses.add(process); + startedProcesses.add(cliProcess); + + return new ExecutionResultAsync(processIO, String.join(" ", fullCommand)); + } + + /** + * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not + * exit the process like the subscribe command. + * @param command the command to execute with the mqtt cli + * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages + * @throws IOException when an error occurred while starting the process + */ + public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command) + throws IOException { + return executeAsync(command, Map.of()); + } + +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index d8e29cb64..6817fa18c 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -43,7 +43,7 @@ public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { private Process orphanCleanupProcess; private int connectClientMarker = 0; - private @NotNull Map envVariables; + private final @NotNull Map envVariables; public MqttCliShell() { envVariables = Map.of(); @@ -62,7 +62,7 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception // Start and await the start of the shell this.process = startShellMode(homeDir); - this.orphanCleanupProcess = startOrphanCleanupProcess(process); + this.orphanCleanupProcess = OrphanProcessCleanup.startOrphanCleanupProcess(process); this.processIO = ProcessIO.startReading(process); new AwaitOutput(processIO, null, String.join(" ", getShellCommand(homeDir))).awaitStdOut("mqtt>"); @@ -76,25 +76,6 @@ public void afterEach(final @NotNull ExtensionContext context) { orphanCleanupProcess.destroyForcibly(); } - private @NotNull Process startOrphanCleanupProcess(final @NotNull Process childProcess) throws IOException { - // We start the ProcessGarbageCollector which sole job is to destroy the childProcess, meaning the mqtt-cli shell, - // when the jvm process exited - final long jvmProcessId = ProcessHandle.current().pid(); - final List orphanCleanupProcessCommand = List.of( - System.getProperty("java"), - Resources.getResource("OrphanCleanupProcess.java").getPath(), - String.valueOf(jvmProcessId), - String.valueOf(childProcess.pid()) - ); - final Process orphanCleanupProcess = new ProcessBuilder(orphanCleanupProcessCommand).start(); - - // Wait until the process prints X, which means that the process garbage collector has registered the - final int readChar = orphanCleanupProcess.getInputStream().read(); - assertEquals('X', readChar); - - return orphanCleanupProcess; - } - private @NotNull Process startShellMode(final @NotNull Path homeDir) throws IOException { final List shellCommand = getShellCommand(homeDir); final ProcessBuilder processBuilder = new ProcessBuilder(shellCommand); diff --git a/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java new file mode 100644 index 000000000..d1fe352d2 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import com.google.common.io.Resources; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class OrphanProcessCleanup { + + public static @NotNull Process startOrphanCleanupProcess(final @NotNull Process childProcess) throws IOException { + // We start the ProcessGarbageCollector which sole job is to destroy the childProcess, meaning the mqtt-cli shell, + // when the jvm process exited + final long jvmProcessId = ProcessHandle.current().pid(); + final List orphanCleanupProcessCommand = List.of( + System.getProperty("java"), + Resources.getResource("OrphanCleanupProcess.java").getPath(), + String.valueOf(jvmProcessId), + String.valueOf(childProcess.pid()) + ); + final Process orphanCleanupProcess = new ProcessBuilder(orphanCleanupProcessCommand).start(); + + // Wait until the process prints X, which means that the process garbage collector has registered the + final int readChar = orphanCleanupProcess.getInputStream().read(); + assertEquals('X', readChar); + + return orphanCleanupProcess; + } +} diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java index 22d6d85ac..9d22fb825 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java @@ -31,6 +31,7 @@ public class SubscribeAssertion { private @NotNull List subscriptions = List.of(); private @NotNull UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.of()); + private SubscribeAssertion() { } From 1e10165fc969376ef3e21285a0753765e72ba282 Mon Sep 17 00:00:00 2001 From: gitseti Date: Wed, 12 Oct 2022 13:59:24 +0200 Subject: [PATCH 52/64] System Tests > Add subscribe tls and websockets tests --- .../cli/subscribe/SubscribeConnectST.java | 607 ++++++++++++++++++ .../cli/subscribe/SubscribeConnectTlsST.java | 89 +++ .../SubscribeConnectWebsocketsST.java | 74 +++ 3 files changed, 770 insertions(+) create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java create mode 100644 src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java new file mode 100644 index 000000000..4830e257d --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -0,0 +1,607 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.subscribe; + +import com.google.common.collect.ImmutableList; +import com.hivemq.cli.utils.*; +import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; +import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; +import com.hivemq.extension.sdk.api.services.builder.Builders; +import com.hivemq.extension.sdk.api.services.builder.WillPublishBuilder; +import com.hivemq.extensions.packets.general.UserPropertiesImpl; +import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static org.junit.jupiter.api.Assertions.*; + +public class SubscribeConnectST { + + @RegisterExtension + private static final HiveMQ hivemq = HiveMQ.builder().build(); + + @RegisterExtension + private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectWrongHost(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", + "-h", + "wrong-host", + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + String.valueOf(mqttVersion), + "-t", + "test", + "-d"); + + final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput() + .contains("wrong-host: nodename nor servname provided, or not known")); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectWrongPort(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of("sub", + "-h", + hivemq.getHost(), + "-p", + "22", + "-V", + String.valueOf(mqttVersion), + "-t", + "test", + "-d"); + + final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); + assertEquals(0, executionResult.getExitCode()); + assertTrue(executionResult.getErrorOutput().contains("Connection refused")); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectCleanStart(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--no-cleanStart"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setCleanStart(false); + if (mqttVersion == '3') { + connectAssertion.setSessionExpiryInterval(4294967295L); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectKeepAlive(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-k"); + subscribeCommand.add("100"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setKeepAlive(100); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectNoClientId(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.remove("-i"); + subscribeCommand.remove("cliTest"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + executionResult.awaitStdOut("sending CONNECT"); + executionResult.awaitStdOut("received CONNACK"); + + final String expectedClientId; + if (mqttVersion == '5') { + final ConnackPacket connackPacket = hivemq.getConnackPackets().get(0); + assertTrue(connackPacket.getAssignedClientIdentifier().isPresent()); + expectedClientId = connackPacket.getAssignedClientIdentifier().get(); + } else { + final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + expectedClientId = connectPacket.getClientId(); + } + + executionResult.awaitStdOut(String.format("Client '%s@%s' sending SUBSCRIBE", expectedClientId, hivemq.getHost())); + executionResult.awaitStdOut(String.format("Client '%s@%s' received SUBACK", expectedClientId, hivemq.getHost())); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId(expectedClientId); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectIdentifierPrefix(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.remove("-i"); + subscribeCommand.remove("cliTest"); + subscribeCommand.add("-ip"); + subscribeCommand.add("test-"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + if (mqttVersion == '3') { + assertTrue(connectPacket.getClientId().startsWith("test-")); + } + + assertConnectPacket(connectPacket, connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setClientId(connectPacket.getClientId()); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserName(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-u"); + subscribeCommand.add("username"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectPassword(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-pw"); + subscribeCommand.add("password"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + } else { + assertSubscribeOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectPasswordEnv(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-pw:env"); + subscribeCommand.add("PASSWORD"); + + final ExecutionResultAsync executionResult = + mqttCli.executeAsync(subscribeCommand, Map.of("PASSWORD", "password")); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + } else { + assertSubscribeOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectPasswordFile(final char mqttVersion) throws Exception { + final Path passwordFile = Files.createTempFile("password-file", ".txt"); + passwordFile.toFile().deleteOnExit(); + Files.writeString(passwordFile, "password"); + + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-pw:file"); + subscribeCommand.add(passwordFile.toString()); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); + } else { + assertSubscribeOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserNameAndPassword(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-u"); + subscribeCommand.add("username"); + subscribeCommand.add("-pw"); + subscribeCommand.add("password"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserNameAndPasswordEnv(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-u"); + subscribeCommand.add("username"); + subscribeCommand.add("-pw:env"); + subscribeCommand.add("PASSWORD"); + + final ExecutionResultAsync executionResult = + mqttCli.executeAsync(subscribeCommand, Map.of("PASSWORD", "password")); + + assertSubscribeOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserNamePasswordFile(final char mqttVersion) throws Exception { + final Path passwordFile = Files.createTempFile("password-file", ".txt"); + passwordFile.toFile().deleteOnExit(); + Files.writeString(passwordFile, "password"); + + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-u"); + subscribeCommand.add("username"); + subscribeCommand.add("-pw:file"); + subscribeCommand.add(passwordFile.toString()); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + + assertSubscribeOutput(executionResult); + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setUserName("username"); + connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectWill(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-Wt"); + subscribeCommand.add("test-will-topic"); + subscribeCommand.add("-Wm"); + subscribeCommand.add("will-message"); + subscribeCommand.add("-Wq"); + subscribeCommand.add("2"); + subscribeCommand.add("-Wr"); + subscribeCommand.add("-We"); + subscribeCommand.add("120"); + subscribeCommand.add("-Wd"); + subscribeCommand.add("180"); + subscribeCommand.add("-Wpf"); + subscribeCommand.add(PayloadFormatIndicator.UTF_8.name()); + subscribeCommand.add("-Wct"); + subscribeCommand.add("content-type"); + subscribeCommand.add("-Wrt"); + subscribeCommand.add("will-response-topic"); + subscribeCommand.add("-Wup"); + subscribeCommand.add("key1=value1"); + subscribeCommand.add("-Wup"); + subscribeCommand.add("key2=value2"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Will Message Expiry was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr("Will Payload Format was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr("Will Delay Interval was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr("Will Content Type was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr("Will Response Topic was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + final WillPublishBuilder expectedWillBuilder = Builders.willPublish() + .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) + .topic("test-will-topic") + .qos(Qos.EXACTLY_ONCE) + .messageExpiryInterval(4294967295L) + .retain(true); + + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setWillPublish(expectedWillBuilder.build()); + }); + + } else { + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + final WillPublishBuilder expectedWillBuilder = Builders.willPublish() + .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) + .topic("test-will-topic") + .qos(Qos.EXACTLY_ONCE) + .retain(true) + .payloadFormatIndicator(PayloadFormatIndicator.UNSPECIFIED) + .messageExpiryInterval(120) + .willDelay(180) + .payloadFormatIndicator(PayloadFormatIndicator.UTF_8) + .contentType("content-type") + .responseTopic("will-response-topic") + .userProperty("key1", "value1") + .userProperty("key2", "value2"); + + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setWillPublish(expectedWillBuilder.build()); + }); + } + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectReceiveMax(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--rcvMax"); + subscribeCommand.add("100"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setReceiveMaximum(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectMaxPacketSize(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--maxPacketSize"); + subscribeCommand.add("100"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setMaximumPacketSize(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--topicAliasMax"); + subscribeCommand.add("100"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setTopicAliasMaximum(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectRequestProblemInformation(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--no-reqProblemInfo"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestProblemInformation(false); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectRequestResponseInformation(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--reqResponseInfo"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestResponseInformation(true); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectSessionExpiryInterval(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-se"); + subscribeCommand.add("100"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setSessionExpiryInterval(100); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectUserProperties(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("-Cup"); + subscribeCommand.add("key1=value1"); + subscribeCommand.add("-Cup"); + subscribeCommand.add("key2=value2"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + if (mqttVersion == '3') { + executionResult.awaitStdErr("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1"); + } + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + if (mqttVersion == '5') { + final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( + MqttUserProperty.of("key1", "value1"), + MqttUserProperty.of("key2", "value2"))); + connectAssertion.setUserProperties(expectedUserProperties); + } + }); + } + + private void assertSubscribeOutput(final @NotNull ExecutionResultAsync executionResultAsync) { + executionResultAsync.awaitStdOut("sending CONNECT"); + executionResultAsync.awaitStdOut("received CONNACK"); + executionResultAsync.awaitStdOut("sending SUBSCRIBE"); + executionResultAsync.awaitStdOut("received SUBACK"); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } + + private @NotNull List defaultSubscribeCommand(final char mqttVersion) { + final ArrayList subscribeCommand = new ArrayList<>(); + subscribeCommand.add("sub"); + subscribeCommand.add("-h"); + subscribeCommand.add(hivemq.getHost()); + subscribeCommand.add("-p"); + subscribeCommand.add(String.valueOf(hivemq.getMqttPort())); + subscribeCommand.add("-V"); + subscribeCommand.add(String.valueOf(mqttVersion)); + subscribeCommand.add("-i"); + subscribeCommand.add("cliTest"); + subscribeCommand.add("-t"); + subscribeCommand.add("test"); + subscribeCommand.add("-d"); + return subscribeCommand; + } + +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java new file mode 100644 index 000000000..f00f20624 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java @@ -0,0 +1,89 @@ +package com.hivemq.cli.commands.cli.subscribe; + +import com.google.common.io.Resources; +import com.hivemq.cli.utils.ExecutionResultAsync; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliAsync; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; +import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; +import com.hivemq.extensions.packets.subscribe.SubscriptionImpl; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; +import static org.junit.jupiter.api.Assertions.fail; + +public class SubscribeConnectTlsST { + + @RegisterExtension + private static final HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); + + @RegisterExtension + private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_mutualTls(final char mqttVersion) throws Exception { + + final String clientKeyPem = Resources.getResource("tls/client-key.pem").getPath(); + final String clientCertPem = Resources.getResource("tls/client-cert.pem").getPath(); + final String serverPem = Resources.getResource("tls/server.pem").getPath(); + + final List subscribeCommand = List.of( + "sub", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttTlsPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "cliTest", + "-t", + "topic", + "--cafile", + serverPem, + "--key", + clientKeyPem, + "--cert", + clientCertPem, + "-d"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + executionResult.awaitStdOut("Enter private key password:"); + executionResult.write("changeme"); + executionResult.awaitStdOut("received CONNACK"); + executionResult.awaitStdOut("received SUBACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("topic", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java new file mode 100644 index 000000000..1bc05d12a --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java @@ -0,0 +1,74 @@ +package com.hivemq.cli.commands.cli.subscribe; + +import com.hivemq.cli.utils.*; +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.extension.sdk.api.packets.general.Qos; +import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; +import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; +import com.hivemq.extensions.packets.subscribe.SubscriptionImpl; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; +import static org.junit.jupiter.api.Assertions.*; + +public class SubscribeConnectWebsocketsST { + + @RegisterExtension + private static final HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); + + @RegisterExtension + private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_websockets(final char mqttVersion) throws Exception { + final List subscribeCommand = List.of( + "sub", + "-h", hivemq.getHost(), + "-p", String.valueOf(hivemq.getWebsocketsPort()), + "-V", String.valueOf(mqttVersion), + "-i", "cliTest", + "-t", "topic", + "-ws", + "-ws:path", + hivemq.getWebsocketsPath(), + "-d" + ); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + executionResult.awaitStdOut("received CONNACK"); + executionResult.awaitStdOut("received SUBACK"); + + assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(toVersion(mqttVersion)); + }); + + assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + final List expectedSubscriptions = + List.of(new SubscriptionImpl("topic", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); + subscribeAssertion.setSubscriptions(expectedSubscriptions); + }); + } + + private @NotNull MqttVersion toVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + fail("version " + version + " can not be converted to MqttVersion object."); + throw new RuntimeException(); + } +} From 5c287d78d186b15a2f06e5c1932993b4db830fbb Mon Sep 17 00:00:00 2001 From: gitseti Date: Wed, 12 Oct 2022 13:59:40 +0200 Subject: [PATCH 53/64] System Tests > Add license headers --- .../cli/subscribe/SubscribeConnectTlsST.java | 15 +++++++++++++++ .../subscribe/SubscribeConnectWebsocketsST.java | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java index f00f20624..47c46626b 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.subscribe; import com.google.common.io.Resources; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java index 1bc05d12a..edf1fb6e8 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.commands.cli.subscribe; import com.hivemq.cli.utils.*; From 6a5c0bb08dff1218f9f8ffcd52fcb646d376cab6 Mon Sep 17 00:00:00 2001 From: gitseti Date: Wed, 12 Oct 2022 15:04:01 +0200 Subject: [PATCH 54/64] System Tests > Fix some tests --- .../cli/publish/PublishConnectST.java | 7 ++- .../cli/subscribe/SubscribeConnectST.java | 7 ++- .../commands/shell/ShellUnsubscribeST.java | 2 +- .../shell/connect/ShellConnectST.java | 57 ++++++++++++------- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index 6675246c3..69e0ab50f 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -70,8 +70,8 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(0, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput() - .contains("wrong-host: nodename nor servname provided, or not known")); + assertTrue(executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || + executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); } @ParameterizedTest @@ -93,7 +93,8 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(0, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Connection refused")); + assertTrue(executionResult.getErrorOutput().contains("readAddress(..) failed: Connection reset by peer") || + executionResult.getErrorOutput().contains("Connection refused")); } @ParameterizedTest diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java index 4830e257d..c9dbee273 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -69,8 +69,8 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(0, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput() - .contains("wrong-host: nodename nor servname provided, or not known")); + assertTrue(executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || + executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); } @ParameterizedTest @@ -90,7 +90,8 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(0, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Connection refused")); + assertTrue(executionResult.getErrorOutput().contains("readAddress(..) failed: Connection reset by peer") || + executionResult.getErrorOutput().contains("Connection refused")); } @ParameterizedTest diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java index 08455da43..d7087de20 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java @@ -124,7 +124,7 @@ void test_missingTopic(final char mqttVersion) throws Exception { mqttCliShell.connectClient(hivemq, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) - .awaitStdErr("Missing required option '--topic '") + .awaitStdErr("Missing required option: '--topic '") .awaitStdErr("Try 'help unsub' for more information."); assertEquals(0, hivemq.getUnsubscribePackets().size()); diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index 9721934d4..d7a8192f6 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -84,19 +84,25 @@ void test_defaultConnect(final char mqttVersion) throws Exception { void test_connectWhileConnected(final char mqttVersion) throws Exception { final List connectCommand1 = List.of( "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-V", String.valueOf(mqttVersion), - "-i", "client1" - ); + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "client1"); final List connectCommand2 = List.of( "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()), - "-V", String.valueOf(mqttVersion), - "-i", "client2" - ); + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "client2"); mqttCliShell.executeAsync(connectCommand1) .awaitStdOut(String.format("client1@%s>", hivemq.getHost())) @@ -140,7 +146,8 @@ void test_wrongPort(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_wrongHost(final char mqttVersion) throws Exception { - final List connectCommand = List.of("con", + final List connectCommand = List.of( + "con", "-h", "unreachable-host", "-p", @@ -320,7 +327,8 @@ void test_requestResponseInformation(final char mqttVersion) throws IOException @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_noClientId(final char mqttVersion) throws Exception { - final List connectCommand = List.of("con", + final List connectCommand = List.of( + "con", "-h", hivemq.getHost(), "-p", @@ -549,7 +557,9 @@ void test_will(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_sessionExpiryInterval(final char mqttVersion) throws Exception { - final List connectCommand = List.of("con", + final String clientId = "sessionTest_V" + mqttVersion; + final List connectCommand = List.of( + "con", "-h", hivemq.getHost(), "-p", @@ -559,25 +569,26 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { "-se", "120", "-i", - "sessionTest", + clientId, "--no-cleanStart"); if (mqttVersion == '3') { mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())) .awaitStdErr("Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1") .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setSessionExpiryInterval(4294967295L); connectAssertion.setCleanStart(false); - connectAssertion.setClientId("sessionTest"); + connectAssertion.setClientId(clientId); }); } else { mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())) .awaitLog("sending CONNECT") .awaitLog("sessionExpiryInterval=120") .awaitLog("received CONNACK") @@ -585,7 +596,7 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); assertConnectPacket(connectPacket1, connectAssertion -> { - connectAssertion.setClientId("sessionTest"); + connectAssertion.setClientId(clientId); connectAssertion.setCleanStart(false); connectAssertion.setSessionExpiryInterval(120); }); @@ -593,7 +604,7 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { mqttCliShell.executeAsync(List.of("dis")).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("sessionTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())) .awaitLog("sending CONNECT") .awaitLog("sessionExpiryInterval=120") .awaitLog("received CONNACK") @@ -601,7 +612,7 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); assertConnectPacket(connectPacket2, connectAssertion -> { - connectAssertion.setClientId("sessionTest"); + connectAssertion.setClientId(clientId); connectAssertion.setCleanStart(false); connectAssertion.setSessionExpiryInterval(120); }); @@ -612,7 +623,8 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_identifierPrefix(final char mqttVersion) throws Exception { - final List connectCommand = List.of("con", + final List connectCommand = List.of( + "con", "-h", hivemq.getHost(), "-p", @@ -676,7 +688,8 @@ void test_userProperties(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_keepAlive(final char mqttVersion) throws Exception { - final List connectCommand = List.of("con", + final List connectCommand = List.of( + "con", "-h", hivemq.getHost(), "-p", From 53d0943b40158a985a6fce55a2a4c9aa3a21df34 Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 17 Oct 2022 08:00:52 +0200 Subject: [PATCH 55/64] System Tests > Refactor version conversion --- .../cli/publish/PublishConnectST.java | 51 ++++++++---------- .../cli/publish/PublishConnectTlsST.java | 20 ++----- .../publish/PublishConnectWebsocketsST.java | 18 ++----- .../cli/commands/cli/publish/PublishST.java | 17 ++---- .../cli/subscribe/SubscribeConnectST.java | 54 ++++++++----------- .../cli/subscribe/SubscribeConnectTlsST.java | 12 +---- .../SubscribeConnectWebsocketsST.java | 12 +---- .../commands/cli/subscribe/SubscribeST.java | 16 ++---- .../cli/commands/shell/ShellListST.java | 19 ++----- .../shell/connect/ShellConnectEnvST.java | 17 ++---- .../shell/connect/ShellConnectST.java | 51 +++++++----------- .../shell/connect/ShellConnectTlsST.java | 15 +----- .../connect/ShellConnectWebsocketsST.java | 16 +----- .../com/hivemq/cli/utils/MqttCliShell.java | 23 +++----- .../cli/utils/MqttVersionConverter.java | 41 ++++++++++++++ 15 files changed, 142 insertions(+), 240 deletions(-) create mode 100644 src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index 69e0ab50f..093c88f99 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -19,6 +19,7 @@ import com.hivemq.cli.utils.ExecutionResult; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.general.MqttVersion; @@ -108,7 +109,7 @@ void test_connectCleanStart(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setCleanStart(false); if (mqttVersion == '3') { connectAssertion.setSessionExpiryInterval(4294967295L); @@ -128,7 +129,7 @@ void test_connectKeepAlive(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setKeepAlive(100); }); } @@ -162,7 +163,7 @@ void test_connectNoClientId(final char mqttVersion) throws Exception { expectedClientId, hivemq.getHost()))); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId(expectedClientId); }); } @@ -186,7 +187,7 @@ void test_connectIdentifierPrefix(final char mqttVersion) throws Exception { } assertConnectPacket(connectPacket, connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId(connectPacket.getClientId()); }); } @@ -203,7 +204,7 @@ void test_connectUserName(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); }); } @@ -225,7 +226,7 @@ void test_connectPassword(final char mqttVersion) throws Exception { } else { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -248,7 +249,7 @@ void test_connectPasswordEnv(final char mqttVersion) throws Exception { } else { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -275,7 +276,7 @@ void test_connectPasswordFile(final char mqttVersion) throws Exception { } else { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -296,7 +297,7 @@ void test_connectUserNameAndPassword(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -316,7 +317,7 @@ void test_connectUserNameAndPasswordEnv(final char mqttVersion) throws Exception assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -340,7 +341,7 @@ void test_connectUserNamePasswordFile(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -398,7 +399,7 @@ void test_connectWill(final char mqttVersion) throws Exception { .messageExpiryInterval(4294967295L) .retain(true); - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setWillPublish(expectedWillBuilder.build()); }); @@ -418,7 +419,7 @@ void test_connectWill(final char mqttVersion) throws Exception { .userProperty("key1", "value1") .userProperty("key2", "value2"); - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setWillPublish(expectedWillBuilder.build()); }); } @@ -441,7 +442,7 @@ void test_connectReceiveMax(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setReceiveMaximum(100); } @@ -465,7 +466,7 @@ void test_connectMaxPacketSize(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setMaximumPacketSize(100); } @@ -489,7 +490,7 @@ void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setTopicAliasMaximum(100); } @@ -512,7 +513,7 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestProblemInformation(false); } @@ -536,7 +537,7 @@ void test_connectRequestResponseInformation(final char mqttVersion) throws Excep } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestResponseInformation(true); } @@ -560,7 +561,7 @@ void test_connectSessionExpiryInterval(final char mqttVersion) throws Exception } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setSessionExpiryInterval(100); } @@ -585,7 +586,7 @@ void test_connectUserProperties(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( MqttUserProperty.of("key1", "value1"), @@ -603,16 +604,6 @@ private void assertPublishOutput(final @NotNull ExecutionResult executionResult) assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } - private @NotNull List defaultPublishCommand(final char mqttVersion) { final ArrayList publishCommand = new ArrayList<>(); publishCommand.add("pub"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java index d156a1075..53290f792 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java @@ -16,9 +16,10 @@ package com.hivemq.cli.commands.cli.publish; import com.google.common.io.Resources; -import com.hivemq.cli.utils.*; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; -import org.jetbrains.annotations.NotNull; +import com.hivemq.cli.utils.ExecutionResultAsync; +import com.hivemq.cli.utils.HiveMQ; +import com.hivemq.cli.utils.MqttCliAsync; +import com.hivemq.cli.utils.MqttVersionConverter; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -31,7 +32,6 @@ import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; -import static org.junit.jupiter.api.Assertions.fail; public class PublishConnectTlsST { @@ -73,7 +73,7 @@ void test_mutualTls(final char mqttVersion) throws Exception { executionResult.awaitStdOut("received PUBLISH acknowledgement"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); }); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { @@ -82,14 +82,4 @@ void test_mutualTls(final char mqttVersion) throws Exception { }); } - - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java index 54c53c9de..24329cf4e 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java @@ -18,9 +18,7 @@ import com.hivemq.cli.utils.ExecutionResult; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; -import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; -import org.jetbrains.annotations.NotNull; +import com.hivemq.cli.utils.MqttVersionConverter; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -33,7 +31,8 @@ import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class PublishConnectWebsocketsST { @@ -64,7 +63,7 @@ void test_websockets(final char mqttVersion) throws Exception { assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); }); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { @@ -73,14 +72,5 @@ void test_websockets(final char mqttVersion) throws Exception { }); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java index 5462663a3..5e4765a75 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java @@ -20,7 +20,7 @@ import com.hivemq.cli.utils.ExecutionResult; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCli; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; @@ -46,7 +46,8 @@ import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class PublishST { @@ -63,7 +64,7 @@ void test_successfulConnectAndPublish(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); }); assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { @@ -386,16 +387,6 @@ private void assertPublishOutput(final @NotNull ExecutionResult executionResult) assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } - private @NotNull List defaultPublishCommand(final char mqttVersion) { final ArrayList publishCommand = new ArrayList<>(); publishCommand.add("pub"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java index c9dbee273..454b898e9 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -19,7 +19,6 @@ import com.hivemq.cli.utils.*; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; import com.hivemq.extension.sdk.api.services.builder.Builders; @@ -42,7 +41,8 @@ import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SubscribeConnectST { @@ -105,7 +105,7 @@ void test_connectCleanStart(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setCleanStart(false); if (mqttVersion == '3') { connectAssertion.setSessionExpiryInterval(4294967295L); @@ -125,7 +125,7 @@ void test_connectKeepAlive(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setKeepAlive(100); }); } @@ -156,7 +156,7 @@ void test_connectNoClientId(final char mqttVersion) throws Exception { executionResult.awaitStdOut(String.format("Client '%s@%s' received SUBACK", expectedClientId, hivemq.getHost())); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId(expectedClientId); }); } @@ -180,7 +180,7 @@ void test_connectIdentifierPrefix(final char mqttVersion) throws Exception { } assertConnectPacket(connectPacket, connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId(connectPacket.getClientId()); }); } @@ -197,7 +197,7 @@ void test_connectUserName(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); }); } @@ -217,7 +217,7 @@ void test_connectPassword(final char mqttVersion) throws Exception { } else { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -239,7 +239,7 @@ void test_connectPasswordEnv(final char mqttVersion) throws Exception { } else { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -264,7 +264,7 @@ void test_connectPasswordFile(final char mqttVersion) throws Exception { } else { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -284,7 +284,7 @@ void test_connectUserNameAndPassword(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -305,7 +305,7 @@ void test_connectUserNameAndPasswordEnv(final char mqttVersion) throws Exception assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -329,7 +329,7 @@ void test_connectUserNamePasswordFile(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -381,7 +381,7 @@ void test_connectWill(final char mqttVersion) throws Exception { .messageExpiryInterval(4294967295L) .retain(true); - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setWillPublish(expectedWillBuilder.build()); }); @@ -401,7 +401,7 @@ void test_connectWill(final char mqttVersion) throws Exception { .userProperty("key1", "value1") .userProperty("key2", "value2"); - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setWillPublish(expectedWillBuilder.build()); }); } @@ -423,7 +423,7 @@ void test_connectReceiveMax(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setReceiveMaximum(100); } @@ -446,7 +446,7 @@ void test_connectMaxPacketSize(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setMaximumPacketSize(100); } @@ -469,7 +469,7 @@ void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setTopicAliasMaximum(100); } @@ -491,7 +491,7 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestProblemInformation(false); } @@ -513,7 +513,7 @@ void test_connectRequestResponseInformation(final char mqttVersion) throws Excep } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestResponseInformation(true); } @@ -536,7 +536,7 @@ void test_connectSessionExpiryInterval(final char mqttVersion) throws Exception } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setSessionExpiryInterval(100); } @@ -561,7 +561,7 @@ void test_connectUserProperties(final char mqttVersion) throws Exception { } assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( MqttUserProperty.of("key1", "value1"), @@ -578,16 +578,6 @@ private void assertSubscribeOutput(final @NotNull ExecutionResultAsync execution executionResultAsync.awaitStdOut("received SUBACK"); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } - private @NotNull List defaultSubscribeCommand(final char mqttVersion) { final ArrayList subscribeCommand = new ArrayList<>(); subscribeCommand.add("sub"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java index 47c46626b..3ccf0c3b6 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java @@ -19,6 +19,7 @@ import com.hivemq.cli.utils.ExecutionResultAsync; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliAsync; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; @@ -81,7 +82,7 @@ void test_mutualTls(final char mqttVersion) throws Exception { executionResult.awaitStdOut("received SUBACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); }); assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { @@ -92,13 +93,4 @@ void test_mutualTls(final char mqttVersion) throws Exception { } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java index edf1fb6e8..3efe4f57f 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java @@ -67,7 +67,7 @@ void test_websockets(final char mqttVersion) throws Exception { executionResult.awaitStdOut("received SUBACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); }); assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { @@ -76,14 +76,4 @@ void test_websockets(final char mqttVersion) throws Exception { subscribeAssertion.setSubscriptions(expectedSubscriptions); }); } - - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java index bc633d124..1a284b106 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java @@ -22,7 +22,6 @@ import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; @@ -46,7 +45,8 @@ import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SubscribeST { @@ -70,7 +70,7 @@ void test_successfulConnectAndSubscribe(final char mqttVersion) throws Exception executionResult.awaitStdOut("message"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); }); assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { @@ -382,14 +382,4 @@ private Subscription createSubscription(final @NotNull String topic, final @NotN return new SubscriptionImpl(topic, qos, RetainHandling.SEND, false, false); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } - } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java index 1d0636179..9d480579c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java @@ -18,7 +18,7 @@ import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; -import com.hivemq.client.mqtt.MqttVersion; +import com.hivemq.cli.utils.MqttVersionConverter; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -29,8 +29,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.fail; - public class ShellListST { @RegisterExtension @@ -88,21 +86,21 @@ void test_listLongConnectedClients(final char mqttVersion) throws Exception { .awaitStdOut("client1") .awaitStdOut(hivemq.getHost()) .awaitStdOut(String.valueOf(hivemq.getMqttPort())) - .awaitStdOut(toVersion(mqttVersion).name()) + .awaitStdOut(MqttVersionConverter.toClientVersion(mqttVersion).name()) .awaitStdOut("NO_SSL"); awaitOutput.awaitStdOut("CONNECTED") .awaitStdOut("client2") .awaitStdOut(hivemq.getHost()) .awaitStdOut(String.valueOf(hivemq.getMqttPort())) - .awaitStdOut(toVersion(mqttVersion).name()) + .awaitStdOut(MqttVersionConverter.toClientVersion(mqttVersion).name()) .awaitStdOut("NO_SSL"); awaitOutput.awaitStdOut("CONNECTED") .awaitStdOut("client3") .awaitStdOut(hivemq.getHost()) .awaitStdOut(String.valueOf(hivemq.getMqttPort())) - .awaitStdOut(toVersion(mqttVersion).name()) + .awaitStdOut(MqttVersionConverter.toClientVersion(mqttVersion).name()) .awaitStdOut("NO_SSL"); } @@ -129,13 +127,4 @@ void test_listSubscriptions(final char mqttVersion) throws Exception { .awaitStdOut("-subscribed topics: [topic2]"); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.MQTT_3_1_1; - } else if (version == '5') { - return MqttVersion.MQTT_5_0; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java index a5e69fa1c..3101264b9 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java @@ -18,7 +18,7 @@ import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.cli.utils.MqttVersionConverter; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; @@ -33,7 +33,6 @@ import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static org.junit.jupiter.api.Assertions.fail; public class ShellConnectEnvST { @@ -64,7 +63,7 @@ void test_passwordEnv(final char mqttVersion) throws Exception { .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -86,23 +85,13 @@ void test_userNameAndPasswordEnv(final char mqttVersion) throws Exception { .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("user"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } - private @NotNull List defaultConnectCommand() { final ArrayList defaultConnectCommand = new ArrayList<>(); defaultConnectCommand.add("con"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index d7a8192f6..4fc81a07e 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -20,8 +20,8 @@ import com.hivemq.cli.utils.AwaitOutput; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; import com.hivemq.extension.sdk.api.services.builder.Builders; @@ -46,7 +46,6 @@ import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; public class ShellConnectST { @@ -75,7 +74,7 @@ void test_defaultConnect(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); - assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(toVersion(mqttVersion))); + assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion))); } @ParameterizedTest @@ -111,7 +110,7 @@ void test_connectWhileConnected(final char mqttVersion) throws Exception { final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); assertConnectPacket(connectPacket1, connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId("client1"); }); @@ -122,7 +121,7 @@ void test_connectWhileConnected(final char mqttVersion) throws Exception { final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); assertConnectPacket(connectPacket2, connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId("client2"); }); } @@ -178,7 +177,7 @@ void test_cleanStart(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '3') { connectAssertion.setSessionExpiryInterval(4294967295L); } @@ -206,7 +205,7 @@ void test_receiveMaximum(final char mqttVersion) throws Exception { awaitOutput.awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setReceiveMaximum(500); } @@ -233,7 +232,7 @@ void test_maxPacketSize(final char mqttVersion) throws Exception { awaitOutput.awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setMaximumPacketSize(500); } @@ -260,7 +259,7 @@ void test_topicAliasMaximum(final char mqttVersion) throws Exception { awaitOutput.awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setTopicAliasMaximum(5); } @@ -288,7 +287,7 @@ void test_requestProblemInformation(final char mqttVersion) throws IOException { awaitOutput.awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestProblemInformation(false); } @@ -316,7 +315,7 @@ void test_requestResponseInformation(final char mqttVersion) throws IOException awaitOutput.awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestResponseInformation(true); } @@ -363,7 +362,7 @@ void test_userName(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); }); } @@ -387,7 +386,7 @@ void test_password(final char mqttVersion) throws Exception { .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -408,7 +407,7 @@ void test_userNameAndPassword(final char mqttVersion) throws Exception { .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("user"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -438,7 +437,7 @@ void test_passwordFile(final char mqttVersion) throws Exception { .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); } @@ -464,7 +463,7 @@ void test_userNameAndPasswordFile(final char mqttVersion) throws Exception { .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("user"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -522,7 +521,7 @@ void test_will(final char mqttVersion) throws Exception { .messageExpiryInterval(4294967295L) .retain(true); - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setWillPublish(expectedWillBuilder.build()); }); @@ -546,7 +545,7 @@ void test_will(final char mqttVersion) throws Exception { .userProperty("key1", "value1") .userProperty("key2", "value2"); - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setWillPublish(expectedWillBuilder.build()); }); } @@ -580,7 +579,7 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setSessionExpiryInterval(4294967295L); connectAssertion.setCleanStart(false); connectAssertion.setClientId(clientId); @@ -672,7 +671,7 @@ void test_userProperties(final char mqttVersion) throws Exception { awaitOutput.awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { final ImmutableList userProperties = ImmutableList.builder() .add(new MqttUserProperty("key1", "value1")) @@ -708,21 +707,11 @@ void test_keepAlive(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setKeepAlive(30); }); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } - private @NotNull List defaultConnectCommand() { final ArrayList defaultConnectCommand = new ArrayList<>(); defaultConnectCommand.add("con"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java index a6dd9b53c..b045b86e5 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java @@ -18,8 +18,7 @@ import com.google.common.io.Resources; import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; -import org.jetbrains.annotations.NotNull; +import com.hivemq.cli.utils.MqttVersionConverter; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -29,7 +28,6 @@ import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static org.junit.jupiter.api.Assertions.fail; public class ShellConnectTlsST { @@ -71,18 +69,9 @@ void test_mutualTls(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); }); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java index 803af12fd..cc948b960 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java @@ -17,9 +17,8 @@ import com.hivemq.cli.utils.HiveMQ; import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -29,7 +28,6 @@ import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static org.junit.jupiter.api.Assertions.fail; public class ShellConnectWebsocketsST { @@ -60,16 +58,6 @@ void test_defaultConnect(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); - assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(toVersion(mqttVersion))); - } - - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); + assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion))); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java index 6817fa18c..2282f9206 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java @@ -16,8 +16,6 @@ package com.hivemq.cli.utils; -import com.google.common.io.Resources; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; @@ -31,9 +29,10 @@ import java.util.List; import java.util.Map; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; -import static org.junit.jupiter.api.Assertions.*; +import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { @@ -128,7 +127,7 @@ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, awaitOutput.awaitLog("received CONNACK"); assertConnectPacket(hivemq.getConnectPackets().get(connectClientMarker), connectAssertion -> { - connectAssertion.setMqttVersion(toVersion(mqttVersion)); + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId(clientId); }); @@ -149,18 +148,12 @@ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, return new AwaitOutput(processIO, logWaiter, fullCommand); } + /** + * Check if the mqtt-cli shell process is alive. + * @return true if the mqtt-cli shell process is alive. + */ public boolean isAlive() { return process.isAlive(); } - private @NotNull MqttVersion toVersion(final char version) { - if (version == '3') { - return MqttVersion.V_3_1_1; - } else if (version == '5') { - return MqttVersion.V_5; - } - fail("version " + version + " can not be converted to MqttVersion object."); - throw new RuntimeException(); - } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java b/src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java new file mode 100644 index 000000000..d080bd6e6 --- /dev/null +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-present HiveMQ and the HiveMQ Community + * + * Licensed 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 com.hivemq.cli.utils; + +import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import org.jetbrains.annotations.NotNull; + +public class MqttVersionConverter { + + public static @NotNull MqttVersion toExtensionSdkVersion(final char version) { + if (version == '3') { + return MqttVersion.V_3_1_1; + } else if (version == '5') { + return MqttVersion.V_5; + } + + throw new IllegalArgumentException("version " + version + " can not be converted to MqttVersion object."); + } + + public static @NotNull com.hivemq.client.mqtt.MqttVersion toClientVersion(final char version) { + if (version == '3') { + return com.hivemq.client.mqtt.MqttVersion.MQTT_3_1_1; + } else if (version == '5') { + return com.hivemq.client.mqtt.MqttVersion.MQTT_5_0; + } + throw new IllegalArgumentException("version " + version + " can not be converted to MqttVersion object."); + } +} From 3c90ec962db2e5a6f7835119d859b5a19777bc6b Mon Sep 17 00:00:00 2001 From: gitseti Date: Mon, 17 Oct 2022 08:28:19 +0200 Subject: [PATCH 56/64] System Tests > Refactor package structure --- .../cli/publish/PublishConnectST.java | 9 +++---- .../cli/publish/PublishConnectTlsST.java | 10 +++---- .../publish/PublishConnectWebsocketsST.java | 10 +++---- .../cli/commands/cli/publish/PublishST.java | 10 +++---- .../cli/subscribe/SubscribeConnectST.java | 8 ++++-- .../cli/subscribe/SubscribeConnectTlsST.java | 11 ++++---- .../SubscribeConnectWebsocketsST.java | 14 +++++----- .../commands/cli/subscribe/SubscribeST.java | 10 ++++--- .../cli/commands/shell/ShellDisconnectST.java | 8 +++--- .../cli/commands/shell/ShellExitST.java | 4 +-- .../cli/commands/shell/ShellListST.java | 6 ++--- .../cli/commands/shell/ShellPublishST.java | 8 +++--- .../cli/commands/shell/ShellSubscribeST.java | 8 +++--- .../cli/commands/shell/ShellSwitchST.java | 4 +-- .../commands/shell/ShellUnsubscribeST.java | 8 +++--- .../shell/connect/ShellConnectEnvST.java | 8 +++--- .../shell/connect/ShellConnectST.java | 8 +++--- .../shell/connect/ShellConnectTlsST.java | 6 ++--- .../connect/ShellConnectWebsocketsST.java | 6 ++--- .../cli/utils/OrphanProcessCleanup.java | 4 +-- .../hivemq/cli/utils/{ => broker}/HiveMQ.java | 13 +++------- .../assertions/ConnectAssertion.java | 2 +- .../assertions/DisconnectAssertion.java | 2 +- .../assertions/DisconnectInformation.java | 2 +- .../assertions/PublishAssertion.java | 2 +- .../assertions/SubscribeAssertion.java | 2 +- .../assertions/UnsubscribeAssertion.java | 2 +- .../hivemq/cli/utils/{ => cli}/MqttCli.java | 3 ++- .../cli/utils/{ => cli}/MqttCliAsync.java | 7 +++-- .../cli/utils/{ => cli}/MqttCliShell.java | 26 ++++++++++++++----- .../cli/utils/{ => cli/io}/LogWaiter.java | 3 ++- .../cli/utils/{ => cli/io}/ProcessIO.java | 5 +++- .../utils/{ => cli/results}/AwaitOutput.java | 5 +++- .../{ => cli/results}/ExecutionResult.java | 2 +- .../results}/ExecutionResultAsync.java | 4 ++- .../{ => exceptions}/TimeoutException.java | 2 +- 36 files changed, 134 insertions(+), 108 deletions(-) rename src/systemTest/java/com/hivemq/cli/utils/{ => broker}/HiveMQ.java (95%) rename src/systemTest/java/com/hivemq/cli/utils/{ => broker}/assertions/ConnectAssertion.java (99%) rename src/systemTest/java/com/hivemq/cli/utils/{ => broker}/assertions/DisconnectAssertion.java (98%) rename src/systemTest/java/com/hivemq/cli/utils/{ => broker}/assertions/DisconnectInformation.java (96%) rename src/systemTest/java/com/hivemq/cli/utils/{ => broker}/assertions/PublishAssertion.java (98%) rename src/systemTest/java/com/hivemq/cli/utils/{ => broker}/assertions/SubscribeAssertion.java (97%) rename src/systemTest/java/com/hivemq/cli/utils/{ => broker}/assertions/UnsubscribeAssertion.java (97%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli}/MqttCli.java (97%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli}/MqttCliAsync.java (92%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli}/MqttCliShell.java (85%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli/io}/LogWaiter.java (96%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli/io}/ProcessIO.java (95%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli/results}/AwaitOutput.java (93%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli/results}/ExecutionResult.java (96%) rename src/systemTest/java/com/hivemq/cli/utils/{ => cli/results}/ExecutionResultAsync.java (93%) rename src/systemTest/java/com/hivemq/cli/utils/{ => exceptions}/TimeoutException.java (96%) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index 093c88f99..f50ed6908 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -16,13 +16,12 @@ package com.hivemq.cli.commands.cli.publish; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.ExecutionResult; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCli; +import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCli; import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; import com.hivemq.extension.sdk.api.services.builder.Builders; @@ -44,7 +43,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.*; public class PublishConnectST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java index 53290f792..4a85982e1 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java @@ -16,9 +16,9 @@ package com.hivemq.cli.commands.cli.publish; import com.google.common.io.Resources; -import com.hivemq.cli.utils.ExecutionResultAsync; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliAsync; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliAsync; import com.hivemq.cli.utils.MqttVersionConverter; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; @@ -30,8 +30,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.PublishAssertion.assertPublishPacket; public class PublishConnectTlsST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java index 24329cf4e..d4af49102 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java @@ -15,9 +15,9 @@ */ package com.hivemq.cli.commands.cli.publish; -import com.hivemq.cli.utils.ExecutionResult; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCli; +import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCli; import com.hivemq.cli.utils.MqttVersionConverter; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; @@ -29,8 +29,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.PublishAssertion.assertPublishPacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java index 5e4765a75..58bcd8425 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java @@ -17,9 +17,9 @@ package com.hivemq.cli.commands.cli.publish; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.ExecutionResult; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCli; +import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCli; import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; @@ -44,8 +44,8 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.PublishAssertion.assertPublishPacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java index 454b898e9..2bfd99ab8 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -16,7 +16,11 @@ package com.hivemq.cli.commands.cli.subscribe; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.*; +import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.*; +import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.general.Qos; @@ -40,7 +44,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java index 3ccf0c3b6..f8c8df0d4 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java @@ -16,11 +16,10 @@ package com.hivemq.cli.commands.cli.subscribe; import com.google.common.io.Resources; -import com.hivemq.cli.utils.ExecutionResultAsync; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliAsync; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliAsync; import com.hivemq.cli.utils.MqttVersionConverter; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; @@ -34,8 +33,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.SubscribeAssertion.assertSubscribePacket; import static org.junit.jupiter.api.Assertions.fail; public class SubscribeConnectTlsST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java index 3efe4f57f..b693fd9a4 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java @@ -15,8 +15,10 @@ */ package com.hivemq.cli.commands.cli.subscribe; -import com.hivemq.cli.utils.*; -import com.hivemq.extension.sdk.api.packets.general.MqttVersion; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; +import com.hivemq.cli.utils.cli.MqttCliAsync; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; @@ -27,15 +29,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; -import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; -import static org.junit.jupiter.api.Assertions.*; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.SubscribeAssertion.assertSubscribePacket; public class SubscribeConnectWebsocketsST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java index 1a284b106..1d340f934 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java @@ -18,7 +18,11 @@ import com.google.common.collect.ImmutableList; import com.google.gson.JsonObject; -import com.hivemq.cli.utils.*; +import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.*; +import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; @@ -43,8 +47,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; -import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.SubscribeAssertion.assertSubscribePacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java index 6baaa5d0f..dfa7a02cf 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java @@ -17,9 +17,9 @@ package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.DisconnectAssertion.assertDisconnectPacket; +import static com.hivemq.cli.utils.broker.assertions.DisconnectAssertion.assertDisconnectPacket; import static org.junit.jupiter.api.Assertions.assertTrue; public class ShellDisconnectST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java index 0e00a3a6c..e987e5df7 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java @@ -15,8 +15,8 @@ */ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java index 9d480579c..43e393a4c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java @@ -15,9 +15,9 @@ */ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.cli.utils.MqttVersionConverter; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java index 88ae4274e..1e5692301 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java @@ -17,9 +17,9 @@ package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; @@ -40,7 +40,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.PublishAssertion.assertPublishPacket; +import static com.hivemq.cli.utils.broker.assertions.PublishAssertion.assertPublishPacket; import static org.junit.jupiter.api.Assertions.assertTrue; public class ShellPublishST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java index 05365f454..09b60a02a 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java @@ -18,9 +18,9 @@ import com.google.common.collect.ImmutableList; import com.google.gson.JsonObject; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.client.mqtt.MqttClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.extension.sdk.api.packets.general.Qos; @@ -43,7 +43,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.SubscribeAssertion.assertSubscribePacket; +import static com.hivemq.cli.utils.broker.assertions.SubscribeAssertion.assertSubscribePacket; import static org.junit.jupiter.api.Assertions.assertEquals; public class ShellSubscribeST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java index 9d3d2a593..514c22c34 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java @@ -15,8 +15,8 @@ */ package com.hivemq.cli.commands.shell; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java index d7087de20..f8b85d526 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java @@ -16,9 +16,9 @@ package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; @@ -31,7 +31,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.UnsubscribeAssertion.assertUnsubscribePacket; +import static com.hivemq.cli.utils.broker.assertions.UnsubscribeAssertion.assertUnsubscribePacket; import static org.junit.jupiter.api.Assertions.assertEquals; public class ShellUnsubscribeST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java index 3101264b9..99b847b54 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java @@ -15,9 +15,9 @@ */ package com.hivemq.cli.commands.shell.connect; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.cli.utils.MqttVersionConverter; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; @@ -32,7 +32,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; public class ShellConnectEnvST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index 4fc81a07e..566b8bd89 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -17,9 +17,9 @@ package com.hivemq.cli.commands.shell.connect; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.AwaitOutput; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.general.Qos; @@ -44,7 +44,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.assertEquals; public class ShellConnectST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java index b045b86e5..3f4d65aa9 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java @@ -16,8 +16,8 @@ package com.hivemq.cli.commands.shell.connect; import com.google.common.io.Resources; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.cli.utils.MqttVersionConverter; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; @@ -27,7 +27,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; public class ShellConnectTlsST { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java index cc948b960..5ccab3b4e 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java @@ -15,8 +15,8 @@ */ package com.hivemq.cli.commands.shell.connect; -import com.hivemq.cli.utils.HiveMQ; -import com.hivemq.cli.utils.MqttCliShell; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.MqttCliShell; import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import org.junit.jupiter.api.Timeout; @@ -27,7 +27,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; public class ShellConnectWebsocketsST { diff --git a/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java index d1fe352d2..837fe4e35 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java +++ b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java @@ -26,7 +26,7 @@ public class OrphanProcessCleanup { public static @NotNull Process startOrphanCleanupProcess(final @NotNull Process childProcess) throws IOException { - // We start the ProcessGarbageCollector which sole job is to destroy the childProcess, meaning the mqtt-cli shell, + // We start the the OrphanCleanupProcess which sole job is to destroy the childProcess, meaning the mqtt-cli shell, // when the jvm process exited final long jvmProcessId = ProcessHandle.current().pid(); final List orphanCleanupProcessCommand = List.of( @@ -37,7 +37,7 @@ public class OrphanProcessCleanup { ); final Process orphanCleanupProcess = new ProcessBuilder(orphanCleanupProcessCommand).start(); - // Wait until the process prints X, which means that the process garbage collector has registered the + // Wait until the process prints X, which means that the orphan cleanup process has sucessfully started final int readChar = orphanCleanupProcess.getInputStream().read(); assertEquals('X', readChar); diff --git a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java similarity index 95% rename from src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java rename to src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java index 5c6df445a..e7ec1efb0 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.broker; import com.google.common.io.Resources; -import com.hivemq.cli.utils.assertions.DisconnectInformation; +import com.hivemq.cli.utils.broker.assertions.DisconnectInformation; import com.hivemq.configuration.service.InternalConfigurations; import com.hivemq.embedded.EmbeddedExtension; import com.hivemq.embedded.EmbeddedHiveMQ; @@ -24,12 +24,7 @@ import com.hivemq.extension.sdk.api.ExtensionMain; import com.hivemq.extension.sdk.api.annotations.NotNull; import com.hivemq.extension.sdk.api.interceptor.connack.ConnackOutboundInterceptor; -import com.hivemq.extension.sdk.api.interceptor.connack.parameter.ConnackOutboundInput; -import com.hivemq.extension.sdk.api.interceptor.connack.parameter.ConnackOutboundOutput; import com.hivemq.extension.sdk.api.interceptor.connect.ConnectInboundInterceptor; -import com.hivemq.extension.sdk.api.interceptor.unsubscribe.UnsubscribeInboundInterceptor; -import com.hivemq.extension.sdk.api.interceptor.unsubscribe.parameter.UnsubscribeInboundInput; -import com.hivemq.extension.sdk.api.interceptor.unsubscribe.parameter.UnsubscribeInboundOutput; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; @@ -81,7 +76,7 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCal return new Builder(); } - public HiveMQ(final boolean tlsEnabled, final boolean websocketEnabled) { + private HiveMQ(final boolean tlsEnabled, final boolean websocketEnabled) { this.tlsEnabled = tlsEnabled; this.websocketEnabled = websocketEnabled; } @@ -140,7 +135,7 @@ public void extensionStart( Services.interceptorRegistry().setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); Services.interceptorRegistry().setConnackOutboundInterceptorProvider(input -> connackOutboundInterceptor); - // Add all other interceptors + // Add all the other interceptors Services.initializerRegistry().setClientInitializer((initializerInput, clientContext) -> { clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> { diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/ConnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/ConnectAssertion.java similarity index 99% rename from src/systemTest/java/com/hivemq/cli/utils/assertions/ConnectAssertion.java rename to src/systemTest/java/com/hivemq/cli/utils/broker/assertions/ConnectAssertion.java index 5af1ecddf..7acb962a3 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/ConnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/ConnectAssertion.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hivemq.cli.utils.assertions; +package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; import com.hivemq.extension.sdk.api.annotations.Nullable; diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectAssertion.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java rename to src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectAssertion.java index 7b0b6e224..9ee0257c8 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectAssertion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils.assertions; +package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectInformation.java similarity index 96% rename from src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java rename to src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectInformation.java index e6352c40c..acc1f729e 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/DisconnectInformation.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectInformation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils.assertions; +package com.hivemq.cli.utils.broker.assertions; import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; import org.jetbrains.annotations.NotNull; diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/PublishAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/PublishAssertion.java similarity index 98% rename from src/systemTest/java/com/hivemq/cli/utils/assertions/PublishAssertion.java rename to src/systemTest/java/com/hivemq/cli/utils/broker/assertions/PublishAssertion.java index 80504d6d8..8489b7c7e 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/PublishAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/PublishAssertion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils.assertions; +package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; import com.hivemq.extension.sdk.api.packets.general.Qos; diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/SubscribeAssertion.java similarity index 97% rename from src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java rename to src/systemTest/java/com/hivemq/cli/utils/broker/assertions/SubscribeAssertion.java index 9d22fb825..90fc2979a 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/SubscribeAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/SubscribeAssertion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils.assertions; +package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; import com.hivemq.extension.sdk.api.packets.general.UserProperties; diff --git a/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/UnsubscribeAssertion.java similarity index 97% rename from src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java rename to src/systemTest/java/com/hivemq/cli/utils/broker/assertions/UnsubscribeAssertion.java index bc217cff6..161d16abc 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/assertions/UnsubscribeAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/UnsubscribeAssertion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils.assertions; +package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; import com.hivemq.extension.sdk.api.packets.general.UserProperties; diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCli.java similarity index 97% rename from src/systemTest/java/com/hivemq/cli/utils/MqttCli.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/MqttCli.java index b6953d039..3f556ce7d 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCli.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCli.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.cli; +import com.hivemq.cli.utils.cli.results.ExecutionResult; import org.jetbrains.annotations.NotNull; import java.io.IOException; diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliAsync.java b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliAsync.java similarity index 92% rename from src/systemTest/java/com/hivemq/cli/utils/MqttCliAsync.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliAsync.java index fb5d5d38e..ea376fe70 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliAsync.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliAsync.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.cli; +import com.hivemq.cli.utils.OrphanProcessCleanup; +import com.hivemq.cli.utils.cli.io.ProcessIO; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -24,7 +27,7 @@ import java.util.List; import java.util.Map; -import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; +import static com.hivemq.cli.utils.cli.MqttCli.CLI_EXEC; import static org.junit.jupiter.api.Assertions.assertTrue; public class MqttCliAsync implements AfterEachCallback { diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java similarity index 85% rename from src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java index 2282f9206..5517b053f 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java @@ -14,8 +14,14 @@ * limitations under the License. */ -package com.hivemq.cli.utils; - +package com.hivemq.cli.utils.cli; + +import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.OrphanProcessCleanup; +import com.hivemq.cli.utils.broker.HiveMQ; +import com.hivemq.cli.utils.cli.io.LogWaiter; +import com.hivemq.cli.utils.cli.io.ProcessIO; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; @@ -29,13 +35,15 @@ import java.util.List; import java.util.Map; -import static com.hivemq.cli.utils.MqttCli.CLI_EXEC; -import static com.hivemq.cli.utils.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.cli.MqttCli.CLI_EXEC; +import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { + public static String DEFAULT_CLIENT_NAME = "cliTest"; + private ProcessIO processIO; private Process process; private LogWaiter logWaiter; @@ -108,15 +116,21 @@ public void afterEach(final @NotNull ExtensionContext context) { } /** - * Connects a mqtt-client and awaits the successful output statements on std-out and in the logfile. + * Connects a mqtt-client with client-id {@link #DEFAULT_CLIENT_NAME} and awaits the successful output statements on std-out and in the logfile. * * @param hivemq the HiveMQ instance to which the client should connect * @throws IOException when the cli command to connect could not be written to the shell */ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion) throws IOException { - connectClient(hivemq, mqttVersion, "cliTest"); + connectClient(hivemq, mqttVersion, DEFAULT_CLIENT_NAME); } + /** + * Connects a mqtt-client with the given client-id and awaits the successful output statements on std-out and in the logfile. + * + * @param hivemq the HiveMQ instance to which the client should connect + * @throws IOException when the cli command to connect could not be written to the shell + */ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, final @NotNull String clientId) throws IOException { final List connectCommand = List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-V", String.valueOf(mqttVersion), "-i", clientId); diff --git a/src/systemTest/java/com/hivemq/cli/utils/LogWaiter.java b/src/systemTest/java/com/hivemq/cli/utils/cli/io/LogWaiter.java similarity index 96% rename from src/systemTest/java/com/hivemq/cli/utils/LogWaiter.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/io/LogWaiter.java index 2ff1e8427..cce35b347 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/LogWaiter.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/io/LogWaiter.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.cli.io; +import com.hivemq.cli.utils.exceptions.TimeoutException; import org.awaitility.core.ConditionTimeoutException; import org.jetbrains.annotations.NotNull; diff --git a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/cli/io/ProcessIO.java similarity index 95% rename from src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/io/ProcessIO.java index 1489fa249..1de97d32a 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ProcessIO.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/io/ProcessIO.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.cli.io; +import com.hivemq.cli.utils.exceptions.TimeoutException; import org.awaitility.core.ConditionTimeoutException; import org.jetbrains.annotations.NotNull; @@ -140,10 +141,12 @@ public void awaitStdErr(final @NotNull String stdErrMessage) throws TimeoutExcep } public void writeMsg(final @NotNull String message) throws IOException { + // We write these messages to stdout so that the console output of the tests make sense stdoutWriter.write(message); stdoutWriter.write('\n'); stdoutWriter.flush(); + // This is the actual writing to the process which gets interpreted processOutputWriter.write(message); processOutputWriter.write('\n'); processOutputWriter.flush(); diff --git a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java b/src/systemTest/java/com/hivemq/cli/utils/cli/results/AwaitOutput.java similarity index 93% rename from src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/results/AwaitOutput.java index 82bbda9af..6743dbfdb 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/AwaitOutput.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/results/AwaitOutput.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.cli.results; +import com.hivemq.cli.utils.cli.io.LogWaiter; +import com.hivemq.cli.utils.cli.io.ProcessIO; +import com.hivemq.cli.utils.exceptions.TimeoutException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Assertions; diff --git a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResult.java similarity index 96% rename from src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResult.java index bdb4f27a2..b33d80a1f 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResult.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.cli.results; import org.jetbrains.annotations.NotNull; diff --git a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResultAsync.java similarity index 93% rename from src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java rename to src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResultAsync.java index 770c656ca..69c71e35c 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/ExecutionResultAsync.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResultAsync.java @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.cli.results; +import com.hivemq.cli.utils.cli.io.ProcessIO; +import com.hivemq.cli.utils.exceptions.TimeoutException; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; diff --git a/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java b/src/systemTest/java/com/hivemq/cli/utils/exceptions/TimeoutException.java similarity index 96% rename from src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java rename to src/systemTest/java/com/hivemq/cli/utils/exceptions/TimeoutException.java index a67fcf771..bc82930fd 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/TimeoutException.java +++ b/src/systemTest/java/com/hivemq/cli/utils/exceptions/TimeoutException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +package com.hivemq.cli.utils.exceptions; import org.jetbrains.annotations.NotNull; From ed6f108390d7b414339d86657f0abda5aa74c9ba Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Sat, 29 Oct 2022 16:38:56 +0200 Subject: [PATCH 57/64] System Test > Cleanup directories after tests --- .../java/com/hivemq/cli/utils/broker/HiveMQ.java | 13 ++++++++++--- .../java/com/hivemq/cli/utils/cli/MqttCliShell.java | 9 +++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java index e7ec1efb0..516f5b3f9 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java @@ -36,6 +36,7 @@ import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; import com.hivemq.migration.meta.PersistenceType; +import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; @@ -58,6 +59,10 @@ public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCal private EmbeddedHiveMQ hivemq; + private Path hivemqConfigFolder; + private Path hivemqDataFolder; + + private List connectPackets; private List connackPackets; private List publishPackets; @@ -107,13 +112,13 @@ public void beforeAll(final ExtensionContext context) throws IOException { " \n" + ""; - final Path hivemqConfigFolder = Files.createTempDirectory("hivemq-config-folder"); + this.hivemqConfigFolder = Files.createTempDirectory("hivemq-config-folder"); hivemqConfigFolder.toFile().deleteOnExit(); final File configXml = new File(hivemqConfigFolder.toAbsolutePath().toString(), "config.xml"); assertTrue(configXml.createNewFile()); Files.writeString(configXml.toPath(), hivemqConfig); - final Path hivemqDataFolder = Files.createTempDirectory("hivemq-data-folder"); + this.hivemqDataFolder = Files.createTempDirectory("hivemq-data-folder"); hivemqDataFolder.toFile().deleteOnExit(); final EmbeddedExtension embeddedExtension = EmbeddedExtension.builder() @@ -180,8 +185,10 @@ public void extensionStop( } @Override - public void afterAll(final ExtensionContext context) { + public void afterAll(final ExtensionContext context) throws IOException { hivemq.stop(); + FileUtils.deleteDirectory(hivemqConfigFolder.toFile()); + FileUtils.deleteDirectory(hivemqDataFolder.toFile()); } @Override diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java index 5517b053f..aea890e77 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java @@ -22,6 +22,7 @@ import com.hivemq.cli.utils.cli.io.LogWaiter; import com.hivemq.cli.utils.cli.io.ProcessIO; import com.hivemq.cli.utils.cli.results.AwaitOutput; +import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; @@ -44,6 +45,7 @@ public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { public static String DEFAULT_CLIENT_NAME = "cliTest"; + private Path homeDir; private ProcessIO processIO; private Process process; private LogWaiter logWaiter; @@ -62,10 +64,8 @@ public MqttCliShell(final @NotNull Map envVariables) { @Override public void beforeEach(final @NotNull ExtensionContext context) throws Exception { - // Setup the mqtt-cli home folder for logging, etc. - final Path homeDir = Files.createTempDirectory("mqtt-cli-home"); - homeDir.toFile().deleteOnExit(); + this.homeDir = Files.createTempDirectory("mqtt-cli-home"); // Start and await the start of the shell this.process = startShellMode(homeDir); @@ -78,7 +78,8 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception } @Override - public void afterEach(final @NotNull ExtensionContext context) { + public void afterEach(final @NotNull ExtensionContext context) throws IOException { + FileUtils.deleteDirectory(homeDir.toFile()); process.destroyForcibly(); orphanCleanupProcess.destroyForcibly(); } From be93d5e952433cd40f029d3153d06fdcdfa27225 Mon Sep 17 00:00:00 2001 From: Till Seeberger Date: Sat, 29 Oct 2022 16:55:13 +0200 Subject: [PATCH 58/64] System Test > Fix warning --- .../hivemq/cli/commands/AbstractConnectRestrictionFlags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hivemq/cli/commands/AbstractConnectRestrictionFlags.java b/src/main/java/com/hivemq/cli/commands/AbstractConnectRestrictionFlags.java index 64a8b86b0..ce86fab46 100644 --- a/src/main/java/com/hivemq/cli/commands/AbstractConnectRestrictionFlags.java +++ b/src/main/java/com/hivemq/cli/commands/AbstractConnectRestrictionFlags.java @@ -101,7 +101,7 @@ public void logUnusedOptions() { Logger.warn("Restriction send topic alias maximum was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); } - if (requestProblemInformation) { + if (!requestProblemInformation) { Logger.warn("Restriction request problem information was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); } From 5979d1eba6bba18049799f91799672487f5cc6c5 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Tue, 15 Nov 2022 15:31:23 +0100 Subject: [PATCH 59/64] Updated check action --- .github/workflows/check.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 560ce4904..dc3887fa0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -11,14 +11,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: - distribution: 'adopt' - java-version: '11' + distribution: temurin + java-version: 11 + cache: gradle - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} From 7ee1a3017725712a198e582f4b51d4bd8e0cb1a0 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Thu, 15 Dec 2022 11:14:23 +0100 Subject: [PATCH 60/64] Reworked systemTests. --- .../cli/publish/PublishConnectST.java | 78 +++--- .../cli/publish/PublishConnectTlsST.java | 44 ++-- .../publish/PublishConnectWebsocketsST.java | 44 ++-- .../cli/commands/cli/publish/PublishST.java | 90 ++++--- .../cli/subscribe/SubscribeConnectST.java | 91 ++++--- .../cli/subscribe/SubscribeConnectTlsST.java | 26 +- .../SubscribeConnectWebsocketsST.java | 36 +-- .../commands/cli/subscribe/SubscribeST.java | 71 +++--- .../cli/commands/shell/ShellDisconnectST.java | 68 +++--- .../cli/commands/shell/ShellExitST.java | 9 +- .../cli/commands/shell/ShellListST.java | 63 ++--- .../cli/commands/shell/ShellPublishST.java | 167 +++++++------ .../cli/commands/shell/ShellSubscribeST.java | 231 ++++++++---------- .../cli/commands/shell/ShellSwitchST.java | 34 +-- .../commands/shell/ShellUnsubscribeST.java | 65 ++--- .../shell/connect/ShellConnectEnvST.java | 25 +- .../shell/connect/ShellConnectST.java | 146 ++++++----- .../shell/connect/ShellConnectTlsST.java | 40 +-- .../connect/ShellConnectWebsocketsST.java | 33 ++- .../cli/utils/MqttVersionConverter.java | 2 +- .../cli/utils/OrphanProcessCleanup.java | 6 +- .../com/hivemq/cli/utils/broker/HiveMQ.java | 128 +++++----- .../broker/assertions/ConnectAssertion.java | 27 +- .../assertions/DisconnectAssertion.java | 35 ++- .../assertions/DisconnectInformation.java | 2 + .../broker/assertions/PublishAssertion.java | 47 ++-- .../broker/assertions/SubscribeAssertion.java | 5 +- .../assertions/UnsubscribeAssertion.java | 2 +- .../com/hivemq/cli/utils/cli/MqttCli.java | 31 ++- .../hivemq/cli/utils/cli/MqttCliAsync.java | 20 +- .../hivemq/cli/utils/cli/MqttCliShell.java | 57 +++-- .../hivemq/cli/utils/cli/io/LogWaiter.java | 47 ++-- .../hivemq/cli/utils/cli/io/ProcessIO.java | 3 +- .../cli/utils/cli/results/AwaitOutput.java | 23 +- .../utils/cli/results/ExecutionResult.java | 5 +- .../cli/results/ExecutionResultAsync.java | 13 +- .../utils/exceptions/TimeoutException.java | 1 + 37 files changed, 944 insertions(+), 871 deletions(-) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index f50ed6908..8dfd35375 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.cli.publish; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCli; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.ExecutionResult; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.general.Qos; @@ -44,13 +45,14 @@ import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class PublishConnectST { +class PublishConnectST { @RegisterExtension - private static final HiveMQ hivemq = HiveMQ.builder().build(); - + private static final @NotNull HiveMQ HIVEMQ = HiveMQ.builder().build(); + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -59,7 +61,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { "-h", "wrong-host", "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVEMQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-t", @@ -70,8 +72,9 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(0, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || - executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); + assertTrue( + executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || + executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); } @ParameterizedTest @@ -80,7 +83,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { void test_connectWrongPort(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-h", - hivemq.getHost(), + HIVEMQ.getHost(), "-p", "22", "-V", @@ -107,7 +110,7 @@ void test_connectCleanStart(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setCleanStart(false); if (mqttVersion == '3') { @@ -127,7 +130,7 @@ void test_connectKeepAlive(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setKeepAlive(100); }); @@ -147,21 +150,21 @@ void test_connectNoClientId(final char mqttVersion) throws Exception { final String expectedClientId; if (mqttVersion == '5') { - final ConnackPacket connackPacket = hivemq.getConnackPackets().get(0); + final ConnackPacket connackPacket = HIVEMQ.getConnackPackets().get(0); assertTrue(connackPacket.getAssignedClientIdentifier().isPresent()); expectedClientId = connackPacket.getAssignedClientIdentifier().get(); } else { - final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + final ConnectPacket connectPacket = HIVEMQ.getConnectPackets().get(0); expectedClientId = connectPacket.getClientId(); } assertTrue(executionResult.getStandardOutput() - .contains(String.format("Client '%s@%s' sending PUBLISH", expectedClientId, hivemq.getHost()))); + .contains(String.format("Client '%s@%s' sending PUBLISH", expectedClientId, HIVEMQ.getHost()))); assertTrue(executionResult.getStandardOutput() .contains(String.format("Client '%s@%s' received PUBLISH acknowledgement", expectedClientId, - hivemq.getHost()))); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + HIVEMQ.getHost()))); + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId(expectedClientId); }); @@ -180,7 +183,7 @@ void test_connectIdentifierPrefix(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + final ConnectPacket connectPacket = HIVEMQ.getConnectPackets().get(0); if (mqttVersion == '3') { assertTrue(connectPacket.getClientId().startsWith("test-")); } @@ -202,7 +205,7 @@ void test_connectUserName(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); }); @@ -224,7 +227,7 @@ void test_connectPassword(final char mqttVersion) throws Exception { assertEquals(0, executionResult.getExitCode()); } else { assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -247,7 +250,7 @@ void test_connectPasswordEnv(final char mqttVersion) throws Exception { assertEquals(0, executionResult.getExitCode()); } else { assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -274,7 +277,7 @@ void test_connectPasswordFile(final char mqttVersion) throws Exception { assertEquals(0, executionResult.getExitCode()); } else { assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -295,7 +298,7 @@ void test_connectUserNameAndPassword(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -315,7 +318,7 @@ void test_connectUserNameAndPasswordEnv(final char mqttVersion) throws Exception final ExecutionResult executionResult = MqttCli.execute(publishCommand, Map.of("PASSWORD", "password")); assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -339,7 +342,7 @@ void test_connectUserNamePasswordFile(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -390,7 +393,7 @@ void test_connectWill(final char mqttVersion) throws Exception { assertTrue(executionResult.getErrorOutput() .contains("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1")); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { final WillPublishBuilder expectedWillBuilder = Builders.willPublish() .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) .topic("test-will-topic") @@ -403,7 +406,7 @@ void test_connectWill(final char mqttVersion) throws Exception { }); } else { - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { final WillPublishBuilder expectedWillBuilder = Builders.willPublish() .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) .topic("test-will-topic") @@ -440,7 +443,7 @@ void test_connectReceiveMax(final char mqttVersion) throws Exception { .contains("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1")); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setReceiveMaximum(100); @@ -464,7 +467,7 @@ void test_connectMaxPacketSize(final char mqttVersion) throws Exception { .contains("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1")); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setMaximumPacketSize(100); @@ -488,7 +491,7 @@ void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { .contains("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1")); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setTopicAliasMaximum(100); @@ -511,7 +514,7 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except .contains("Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1")); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestProblemInformation(false); @@ -535,7 +538,7 @@ void test_connectRequestResponseInformation(final char mqttVersion) throws Excep "Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1")); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestResponseInformation(true); @@ -559,7 +562,7 @@ void test_connectSessionExpiryInterval(final char mqttVersion) throws Exception .contains("Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1")); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setSessionExpiryInterval(100); @@ -581,10 +584,11 @@ void test_connectUserProperties(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput().contains("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1")); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( @@ -607,9 +611,9 @@ private void assertPublishOutput(final @NotNull ExecutionResult executionResult) final ArrayList publishCommand = new ArrayList<>(); publishCommand.add("pub"); publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); + publishCommand.add(HIVEMQ.getHost()); publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); + publishCommand.add(String.valueOf(HIVEMQ.getMqttPort())); publishCommand.add("-V"); publishCommand.add(String.valueOf(mqttVersion)); publishCommand.add("-i"); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java index 4a85982e1..0c47a9f22 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectTlsST.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.cli.publish; import com.google.common.io.Resources; -import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliAsync; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -33,53 +35,57 @@ import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.broker.assertions.PublishAssertion.assertPublishPacket; -public class PublishConnectTlsST { +class PublishConnectTlsST { @RegisterExtension - private final static HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); + private static final @NotNull HiveMQ HIVEMQ = HiveMQ.builder().withTlsEnabled(true).build(); @RegisterExtension - private final static MqttCliAsync mqttCli = new MqttCliAsync(); + private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_mutualTls(final char mqttVersion) throws Exception { - final String clientKeyPem = Resources.getResource("tls/client-key.pem").getPath(); final String clientCertPem = Resources.getResource("tls/client-cert.pem").getPath(); final String serverPem = Resources.getResource("tls/server.pem").getPath(); final List publishCommand = List.of( "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttTlsPort()), - "-V", String.valueOf(mqttVersion), - "-i", "cliTest", - "-t", "test", - "-m", "message", + "-h", + HIVEMQ.getHost(), + "-p", + String.valueOf(HIVEMQ.getMqttTlsPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "cliTest", + "-t", + "test", + "-m", + "message", "--cafile", serverPem, "--key", clientKeyPem, "--cert", clientCertPem, - "-d" - ); + "-d"); final ExecutionResultAsync executionResult = mqttCli.executeAsync(publishCommand); executionResult.awaitStdOut("Enter private key password:"); executionResult.write("changeme"); executionResult.awaitStdOut("received PUBLISH acknowledgement"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); - }); + assertConnectPacket( + HIVEMQ.getConnectPackets().get(0), + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); }); - } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java index d4af49102..6c6b525ae 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectWebsocketsST.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.cli.publish; -import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCli; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.ExecutionResult; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -34,10 +36,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class PublishConnectWebsocketsST { +class PublishConnectWebsocketsST { @RegisterExtension - private final static HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); + private static final @NotNull HiveMQ HIVEMQ = HiveMQ.builder().withWebsocketEnabled(true).build(); @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @@ -45,32 +47,36 @@ public class PublishConnectWebsocketsST { void test_websockets(final char mqttVersion) throws Exception { final List publishCommand = List.of( "pub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getWebsocketsPort()), - "-V", String.valueOf(mqttVersion), - "-i", "cliTest", - "-t", "topic", - "-m", "message", + "-h", + HIVEMQ.getHost(), + "-p", + String.valueOf(HIVEMQ.getWebsocketsPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "cliTest", + "-t", + "topic", + "-m", + "message", "-ws", "-ws:path", - hivemq.getWebsocketsPath(), - "-d" - ); + HIVEMQ.getWebsocketsPath(), + "-d"); final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(0, executionResult.getExitCode()); assertTrue(executionResult.getStandardOutput().contains("received CONNACK")); assertTrue(executionResult.getStandardOutput().contains("received PUBLISH acknowledgement")); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); - }); + assertConnectPacket( + HIVEMQ.getConnectPackets().get(0), + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("topic"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); }); } - } - diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java index 58bcd8425..9c240cb89 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java @@ -17,10 +17,10 @@ package com.hivemq.cli.commands.cli.publish; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.cli.results.ExecutionResult; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCli; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.ExecutionResult; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; @@ -49,11 +49,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class PublishST { +class PublishST { @RegisterExtension - private static final HiveMQ hivemq = HiveMQ.builder().build(); - + private static final @NotNull HiveMQ HIVEMQ = HiveMQ.builder().build(); + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -63,11 +63,11 @@ void test_successfulConnectAndPublish(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); - }); + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); }); @@ -83,7 +83,7 @@ void test_retain(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setRetain(true); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); @@ -106,7 +106,7 @@ void test_messageFromFile(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message-from-file".getBytes(StandardCharsets.UTF_8))); }); @@ -124,7 +124,7 @@ void test_emptyMessage(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertPublishOutput(executionResult); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.allocate(0)); }); @@ -149,13 +149,11 @@ void test_multipleTopics(final char mqttVersion) throws Exception { assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test2")); assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test3")); - final Set topicSet = hivemq.getPublishPackets() - .stream() - .map(PublishPacket::getTopic) - .collect(Collectors.toSet()); + final Set topicSet = + HIVEMQ.getPublishPackets().stream().map(PublishPacket::getTopic).collect(Collectors.toSet()); assertTrue(topicSet.containsAll(List.of("test1", "test2", "test3"))); - for (final PublishPacket publishPacket : hivemq.getPublishPackets()) { + for (final PublishPacket publishPacket : HIVEMQ.getPublishPackets()) { assertPublishPacket(publishPacket, publishAssertion -> { publishAssertion.setTopic(publishPacket.getTopic()); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); @@ -184,11 +182,17 @@ void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { publishCommand.add("2"); final ExecutionResult executionResult = MqttCli.execute(publishCommand); - assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test1, payload=7byte, qos=AT_MOST_ONCE, retain=false}")); - assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test2, payload=7byte, qos=AT_LEAST_ONCE, retain=false}")); - assertTrue(executionResult.getStandardOutput().contains("sending PUBLISH ('message') MqttPublish{topic=test3, payload=7byte, qos=EXACTLY_ONCE, retain=false}")); - - final Map topicToPublishPacket = hivemq.getPublishPackets() + assertTrue(executionResult.getStandardOutput() + .contains( + "sending PUBLISH ('message') MqttPublish{topic=test1, payload=7byte, qos=AT_MOST_ONCE, retain=false}")); + assertTrue(executionResult.getStandardOutput() + .contains( + "sending PUBLISH ('message') MqttPublish{topic=test2, payload=7byte, qos=AT_LEAST_ONCE, retain=false}")); + assertTrue(executionResult.getStandardOutput() + .contains( + "sending PUBLISH ('message') MqttPublish{topic=test3, payload=7byte, qos=EXACTLY_ONCE, retain=false}")); + + final Map topicToPublishPacket = HIVEMQ.getPublishPackets() .stream() .collect(Collectors.toMap(PublishPacket::getTopic, publishPacket -> publishPacket)); @@ -196,7 +200,7 @@ void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { assertEquals(Qos.AT_LEAST_ONCE, topicToPublishPacket.get("test2").getQos()); assertEquals(Qos.EXACTLY_ONCE, topicToPublishPacket.get("test3").getQos()); - for (final PublishPacket publishPacket : hivemq.getPublishPackets()) { + for (final PublishPacket publishPacket : HIVEMQ.getPublishPackets()) { assertPublishPacket(publishPacket, publishAssertion -> { publishAssertion.setTopic(publishPacket.getTopic()); publishAssertion.setQos(publishPacket.getQos()); @@ -217,10 +221,11 @@ void test_messageExpiryInterval(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput().contains("Publish message expiry was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Publish message expiry was set but is unused in MQTT Version MQTT_3_1_1")); } - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); if (mqttVersion == '5') { @@ -241,10 +246,11 @@ void test_payloadFormatIndicator(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput().contains("Publish payload format indicator was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Publish payload format indicator was set but is unused in MQTT Version MQTT_3_1_1")); } - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); if (mqttVersion == '5') { @@ -265,10 +271,11 @@ void test_contentType(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput().contains("Publish content type was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Publish content type was set but is unused in MQTT Version MQTT_3_1_1")); } - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); if (mqttVersion == '5') { @@ -289,10 +296,11 @@ void test_responseTopic(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput().contains("Publish response topic was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Publish response topic was set but is unused in MQTT Version MQTT_3_1_1")); } - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); if (mqttVersion == '5') { @@ -313,10 +321,11 @@ void test_correlationData(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput().contains("Publish correlation data was set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Publish correlation data was set but is unused in MQTT Version MQTT_3_1_1")); } - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); if (mqttVersion == '5') { @@ -339,10 +348,11 @@ void test_userProperties(final char mqttVersion) throws Exception { assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput().contains("Publish user properties were set but is unused in MQTT Version MQTT_3_1_1")); + assertTrue(executionResult.getErrorOutput() + .contains("Publish user properties were set but is unused in MQTT Version MQTT_3_1_1")); } - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVEMQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setTopic("test"); publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); if (mqttVersion == '5') { @@ -358,7 +368,7 @@ void test_userProperties(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_topic() throws Exception { final List publishCommand = - List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort())); + List.of("pub", "-h", HIVEMQ.getHost(), "-p", String.valueOf(HIVEMQ.getMqttPort())); final ExecutionResult executionResult = MqttCli.execute(publishCommand); @@ -370,7 +380,7 @@ void test_publish_missing_topic() throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_publish_missing_message() throws Exception { final List publishCommand = - List.of("pub", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-t", "test"); + List.of("pub", "-h", HIVEMQ.getHost(), "-p", String.valueOf(HIVEMQ.getMqttPort()), "-t", "test"); final ExecutionResult executionResult = MqttCli.execute(publishCommand); @@ -391,9 +401,9 @@ private void assertPublishOutput(final @NotNull ExecutionResult executionResult) final ArrayList publishCommand = new ArrayList<>(); publishCommand.add("pub"); publishCommand.add("-h"); - publishCommand.add(hivemq.getHost()); + publishCommand.add(HIVEMQ.getHost()); publishCommand.add("-p"); - publishCommand.add(String.valueOf(hivemq.getMqttPort())); + publishCommand.add(String.valueOf(HIVEMQ.getMqttPort())); publishCommand.add("-V"); publishCommand.add(String.valueOf(mqttVersion)); publishCommand.add("-i"); @@ -405,4 +415,4 @@ private void assertPublishOutput(final @NotNull ExecutionResult executionResult) publishCommand.add("-d"); return publishCommand; } -} \ No newline at end of file +} diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java index 2bfd99ab8..21e74a5ab 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.cli.subscribe; import com.google.common.collect.ImmutableList; import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; -import com.hivemq.cli.utils.cli.*; +import com.hivemq.cli.utils.cli.MqttCli; +import com.hivemq.cli.utils.cli.MqttCliAsync; import com.hivemq.cli.utils.cli.results.ExecutionResult; import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; @@ -48,10 +50,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class SubscribeConnectST { +class SubscribeConnectST { @RegisterExtension - private static final HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVEMQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); @@ -64,7 +66,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { "-h", "wrong-host", "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVEMQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-t", @@ -73,8 +75,9 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(0, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || - executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); + assertTrue( + executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || + executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); } @ParameterizedTest @@ -83,7 +86,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { void test_connectWrongPort(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-h", - hivemq.getHost(), + HIVEMQ.getHost(), "-p", "22", "-V", @@ -108,7 +111,7 @@ void test_connectCleanStart(final char mqttVersion) throws Exception { final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setCleanStart(false); if (mqttVersion == '3') { @@ -128,7 +131,7 @@ void test_connectKeepAlive(final char mqttVersion) throws Exception { final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setKeepAlive(100); }); @@ -148,18 +151,22 @@ void test_connectNoClientId(final char mqttVersion) throws Exception { final String expectedClientId; if (mqttVersion == '5') { - final ConnackPacket connackPacket = hivemq.getConnackPackets().get(0); + final ConnackPacket connackPacket = HIVEMQ.getConnackPackets().get(0); assertTrue(connackPacket.getAssignedClientIdentifier().isPresent()); expectedClientId = connackPacket.getAssignedClientIdentifier().get(); } else { - final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + final ConnectPacket connectPacket = HIVEMQ.getConnectPackets().get(0); expectedClientId = connectPacket.getClientId(); } - executionResult.awaitStdOut(String.format("Client '%s@%s' sending SUBSCRIBE", expectedClientId, hivemq.getHost())); - executionResult.awaitStdOut(String.format("Client '%s@%s' received SUBACK", expectedClientId, hivemq.getHost())); + executionResult.awaitStdOut(String.format("Client '%s@%s' sending SUBSCRIBE", + expectedClientId, + HIVEMQ.getHost())); + executionResult.awaitStdOut(String.format("Client '%s@%s' received SUBACK", + expectedClientId, + HIVEMQ.getHost())); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId(expectedClientId); }); @@ -178,7 +185,7 @@ void test_connectIdentifierPrefix(final char mqttVersion) throws Exception { final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribeOutput(executionResult); - final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); + final ConnectPacket connectPacket = HIVEMQ.getConnectPackets().get(0); if (mqttVersion == '3') { assertTrue(connectPacket.getClientId().startsWith("test-")); } @@ -200,7 +207,7 @@ void test_connectUserName(final char mqttVersion) throws Exception { final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); }); @@ -220,7 +227,7 @@ void test_connectPassword(final char mqttVersion) throws Exception { executionResult.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); } else { assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -242,7 +249,7 @@ void test_connectPasswordEnv(final char mqttVersion) throws Exception { executionResult.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); } else { assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -267,7 +274,7 @@ void test_connectPasswordFile(final char mqttVersion) throws Exception { executionResult.awaitStdErr("Password-Only Authentication is not allowed in MQTT 3"); } else { assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -287,7 +294,7 @@ void test_connectUserNameAndPassword(final char mqttVersion) throws Exception { final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -308,7 +315,7 @@ void test_connectUserNameAndPasswordEnv(final char mqttVersion) throws Exception mqttCli.executeAsync(subscribeCommand, Map.of("PASSWORD", "password")); assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -332,7 +339,7 @@ void test_connectUserNamePasswordFile(final char mqttVersion) throws Exception { final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribeOutput(executionResult); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -377,7 +384,7 @@ void test_connectWill(final char mqttVersion) throws Exception { executionResult.awaitStdErr("Will Response Topic was set but is unused in MQTT Version MQTT_3_1_1"); executionResult.awaitStdErr("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { final WillPublishBuilder expectedWillBuilder = Builders.willPublish() .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) .topic("test-will-topic") @@ -390,7 +397,7 @@ void test_connectWill(final char mqttVersion) throws Exception { }); } else { - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { final WillPublishBuilder expectedWillBuilder = Builders.willPublish() .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) .topic("test-will-topic") @@ -426,7 +433,7 @@ void test_connectReceiveMax(final char mqttVersion) throws Exception { executionResult.awaitStdErr("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setReceiveMaximum(100); @@ -446,10 +453,11 @@ void test_connectMaxPacketSize(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); if (mqttVersion == '3') { - executionResult.awaitStdErr("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr( + "Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1"); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setMaximumPacketSize(100); @@ -469,10 +477,11 @@ void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); if (mqttVersion == '3') { - executionResult.awaitStdErr("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr( + "Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1"); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setTopicAliasMaximum(100); @@ -491,10 +500,11 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except assertSubscribeOutput(executionResult); if (mqttVersion == '3') { - executionResult.awaitStdErr("Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr( + "Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1"); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestProblemInformation(false); @@ -513,10 +523,11 @@ void test_connectRequestResponseInformation(final char mqttVersion) throws Excep assertSubscribeOutput(executionResult); if (mqttVersion == '3') { - executionResult.awaitStdErr("Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr( + "Restriction request response information was set but is unused in MQTT Version MQTT_3_1_1"); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestResponseInformation(true); @@ -536,10 +547,11 @@ void test_connectSessionExpiryInterval(final char mqttVersion) throws Exception assertSubscribeOutput(executionResult); if (mqttVersion == '3') { - executionResult.awaitStdErr("Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr( + "Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1"); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setSessionExpiryInterval(100); @@ -561,10 +573,10 @@ void test_connectUserProperties(final char mqttVersion) throws Exception { assertSubscribeOutput(executionResult); if (mqttVersion == '3') { - executionResult.awaitStdErr("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1"); + executionResult.awaitStdErr("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1"); } - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.of( @@ -586,9 +598,9 @@ private void assertSubscribeOutput(final @NotNull ExecutionResultAsync execution final ArrayList subscribeCommand = new ArrayList<>(); subscribeCommand.add("sub"); subscribeCommand.add("-h"); - subscribeCommand.add(hivemq.getHost()); + subscribeCommand.add(HIVEMQ.getHost()); subscribeCommand.add("-p"); - subscribeCommand.add(String.valueOf(hivemq.getMqttPort())); + subscribeCommand.add(String.valueOf(HIVEMQ.getMqttPort())); subscribeCommand.add("-V"); subscribeCommand.add(String.valueOf(mqttVersion)); subscribeCommand.add("-i"); @@ -598,5 +610,4 @@ private void assertSubscribeOutput(final @NotNull ExecutionResultAsync execution subscribeCommand.add("-d"); return subscribeCommand; } - } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java index f8c8df0d4..36b373ef3 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectTlsST.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.cli.subscribe; import com.google.common.io.Resources; -import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliAsync; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; @@ -35,12 +36,11 @@ import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static com.hivemq.cli.utils.broker.assertions.SubscribeAssertion.assertSubscribePacket; -import static org.junit.jupiter.api.Assertions.fail; -public class SubscribeConnectTlsST { +class SubscribeConnectTlsST { @RegisterExtension - private static final HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().withTlsEnabled(true).build(); @RegisterExtension private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); @@ -49,7 +49,6 @@ public class SubscribeConnectTlsST { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_mutualTls(final char mqttVersion) throws Exception { - final String clientKeyPem = Resources.getResource("tls/client-key.pem").getPath(); final String clientCertPem = Resources.getResource("tls/client-cert.pem").getPath(); final String serverPem = Resources.getResource("tls/server.pem").getPath(); @@ -57,9 +56,9 @@ void test_mutualTls(final char mqttVersion) throws Exception { final List subscribeCommand = List.of( "sub", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttTlsPort()), + String.valueOf(HIVE_MQ.getMqttTlsPort()), "-V", String.valueOf(mqttVersion), "-i", @@ -80,16 +79,15 @@ void test_mutualTls(final char mqttVersion) throws Exception { executionResult.awaitStdOut("received CONNACK"); executionResult.awaitStdOut("received SUBACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); - }); + assertConnectPacket( + HIVE_MQ.getConnectPackets().get(0), + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("topic", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - } - } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java index b693fd9a4..3abac1249 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectWebsocketsST.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.cli.subscribe; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; -import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import com.hivemq.cli.utils.cli.MqttCliAsync; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.subscribe.RetainHandling; import com.hivemq.extension.sdk.api.packets.subscribe.Subscription; @@ -38,7 +39,7 @@ public class SubscribeConnectWebsocketsST { @RegisterExtension - private static final HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().withWebsocketEnabled(true).build(); @RegisterExtension private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); @@ -49,26 +50,31 @@ public class SubscribeConnectWebsocketsST { void test_websockets(final char mqttVersion) throws Exception { final List subscribeCommand = List.of( "sub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getWebsocketsPort()), - "-V", String.valueOf(mqttVersion), - "-i", "cliTest", - "-t", "topic", + "-h", + HIVE_MQ.getHost(), + "-p", + String.valueOf(HIVE_MQ.getWebsocketsPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "cliTest", + "-t", + "topic", "-ws", "-ws:path", - hivemq.getWebsocketsPath(), - "-d" - ); + HIVE_MQ.getWebsocketsPath(), + "-d"); final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); executionResult.awaitStdOut("received CONNACK"); executionResult.awaitStdOut("received SUBACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); - }); + assertConnectPacket( + HIVE_MQ.getConnectPackets().get(0), + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("topic", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java index 1d340f934..b34385955 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java @@ -20,7 +20,8 @@ import com.google.gson.JsonObject; import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; -import com.hivemq.cli.utils.cli.*; +import com.hivemq.cli.utils.cli.MqttCli; +import com.hivemq.cli.utils.cli.MqttCliAsync; import com.hivemq.cli.utils.cli.results.ExecutionResult; import com.hivemq.cli.utils.cli.results.ExecutionResultAsync; import com.hivemq.client.mqtt.datatypes.MqttQos; @@ -52,10 +53,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class SubscribeST { +class SubscribeST { @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliAsync mqttCli = new MqttCliAsync(); @@ -73,11 +74,11 @@ void test_successfulConnectAndSubscribe(final char mqttVersion) throws Exception executionResult.awaitStdOut("message"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); - }); + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -94,7 +95,7 @@ void test_qos(final char mqttVersion) throws Exception { final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribe(executionResult); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic", Qos.AT_LEAST_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -129,17 +130,17 @@ void test_multipleTopics(final char mqttVersion) throws Exception { executionResult.awaitStdOut("message3"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic1", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(1), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic2", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(2), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic3", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -180,17 +181,17 @@ void test_multipleTopicsMultipleQoS(final char mqttVersion) throws Exception { executionResult.awaitStdOut("message3"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic1", Qos.AT_MOST_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(1), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic2", Qos.AT_LEAST_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(2), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic3", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -218,7 +219,7 @@ void test_outputFile(final char mqttVersion) throws Exception { assertEquals(1, readLines.size()); assertEquals("message", readLines.get(0)); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -245,7 +246,7 @@ void test_userProperties(final char mqttVersion) throws Exception { executionResult.awaitStdOut("message"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); @@ -273,7 +274,7 @@ void test_base64(final char mqttVersion) throws Exception { final String encodedPayload = Base64.getEncoder().encodeToString("message".getBytes(StandardCharsets.UTF_8)); executionResult.awaitStdOut(encodedPayload); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -293,7 +294,7 @@ void test_showTopic(final char mqttVersion) throws Exception { executionResult.awaitStdOut("topic: message"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -315,19 +316,15 @@ void test_showJson(final char mqttVersion) throws Exception { jsonObject.addProperty("property3", "value3"); publishMessage("topic", jsonObject.toString()); - executionResult.awaitStdOut("{\n" + - " \"topic\": \"topic\",\n" + - " \"payload\": {\n" + - " \"property1\": \"value1\",\n" + - " \"property2\": \"value2\",\n" + - " \"property3\": \"value3\"\n" + - " },\n"); + executionResult.awaitStdOut( + "{\n" + " \"topic\": \"topic\",\n" + " \"payload\": {\n" + " \"property1\": \"value1\",\n" + + " \"property2\": \"value2\",\n" + " \"property3\": \"value3\"\n" + " },\n"); executionResult.awaitStdOut("\"qos\": \"EXACTLY_ONCE\","); executionResult.awaitStdOut("\"receivedAt\":"); executionResult.awaitStdOut("\"retain\": false"); executionResult.awaitStdOut("}"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(createSubscription("topic", Qos.EXACTLY_ONCE)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); @@ -336,11 +333,8 @@ void test_showJson(final char mqttVersion) throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_subscribeMissingTopic() throws Exception { - final List subscribeCommand = List.of( - "sub", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttPort()) - ); + final List subscribeCommand = + List.of("sub", "-h", HIVE_MQ.getHost(), "-p", String.valueOf(HIVE_MQ.getMqttPort())); final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); @@ -352,9 +346,9 @@ void test_subscribeMissingTopic() throws Exception { final ArrayList subscribeCommand = new ArrayList<>(); subscribeCommand.add("sub"); subscribeCommand.add("-h"); - subscribeCommand.add(hivemq.getHost()); + subscribeCommand.add(HIVE_MQ.getHost()); subscribeCommand.add("-p"); - subscribeCommand.add(String.valueOf(hivemq.getMqttPort())); + subscribeCommand.add(String.valueOf(HIVE_MQ.getMqttPort())); subscribeCommand.add("-V"); subscribeCommand.add(String.valueOf(mqttVersion)); subscribeCommand.add("-i"); @@ -375,15 +369,18 @@ private void assertSubscribe(final @NotNull ExecutionResultAsync executionResult private void publishMessage(final @NotNull String topic, final @NotNull String message) { final Mqtt5BlockingClient publisher = Mqtt5Client.builder() .identifier("publisher") - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) + .serverHost(HIVE_MQ.getHost()) + .serverPort(HIVE_MQ.getMqttPort()) .buildBlocking(); publisher.connect(); - publisher.publishWith().topic(topic).payload(message.getBytes(StandardCharsets.UTF_8)).qos(MqttQos.EXACTLY_ONCE).send(); + publisher.publishWith() + .topic(topic) + .payload(message.getBytes(StandardCharsets.UTF_8)) + .qos(MqttQos.EXACTLY_ONCE) + .send(); } private Subscription createSubscription(final @NotNull String topic, final @NotNull Qos qos) { return new SubscriptionImpl(topic, qos, RetainHandling.SEND, false, false); } - } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java index dfa7a02cf..59b53b7a1 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellDisconnectST.java @@ -17,9 +17,9 @@ package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; @@ -36,10 +36,10 @@ import static com.hivemq.cli.utils.broker.assertions.DisconnectAssertion.assertDisconnectPacket; import static org.junit.jupiter.api.Assertions.assertTrue; -public class ShellDisconnectST { +class ShellDisconnectST { @RegisterExtension - private final static @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -49,11 +49,11 @@ public class ShellDisconnectST { @ValueSource(chars = {'3', '5'}) void test_successfulDisconnect(final char mqttVersion) throws Exception { final List disconnectCommand = List.of("dis"); - mqttCliShell.connectClient(hivemq, mqttVersion, "myClient"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "myClient"); mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); - assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { - disconnectAssertion.setDisconnectedClient("myClient"); - }); + assertDisconnectPacket( + HIVE_MQ.getDisconnectInformations().get(0), + disconnectAssertion -> disconnectAssertion.setDisconnectedClient("myClient")); } @ParameterizedTest @@ -63,9 +63,9 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-i", @@ -73,9 +73,9 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { "-se", "120"); final List disconnectCommand = List.of("dis", "-e", "60"); - mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); - assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + assertDisconnectPacket(HIVE_MQ.getDisconnectInformations().get(0), disconnectAssertion -> { if (mqttVersion == '5') { disconnectAssertion.setSessionExpiryInterval(60); } @@ -88,7 +88,7 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { void test_reasonString(final char mqttVersion) throws Exception { final List disconnectCommand = List.of("dis", "-r", "test-reason"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); @@ -96,7 +96,7 @@ void test_reasonString(final char mqttVersion) throws Exception { awaitOutput.awaitStdErr("Reason string was set but is unused in Mqtt version MQTT_3_1_1"); } - assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + assertDisconnectPacket(HIVE_MQ.getDisconnectInformations().get(0), disconnectAssertion -> { if (mqttVersion == '5') { disconnectAssertion.setReasonString("test-reason"); } @@ -109,7 +109,7 @@ void test_reasonString(final char mqttVersion) throws Exception { void test_userProperties(final char mqttVersion) throws Exception { final List disconnectCommand = List.of("dis", "-up", "key1=value1", "-up", "key2=value2"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); @@ -117,7 +117,7 @@ void test_userProperties(final char mqttVersion) throws Exception { awaitOutput.awaitStdErr("User properties were set but are unused in Mqtt version MQTT_3_1_1"); } - assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { + assertDisconnectPacket(HIVE_MQ.getDisconnectInformations().get(0), disconnectAssertion -> { if (mqttVersion == '5') { final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.builder() @@ -136,13 +136,13 @@ void test_disconnectById(final char mqttVersion) throws Exception { final String clientId = "myTestClient"; final List disconnectCommand = List.of("dis", "-i", clientId); - mqttCliShell.connectClient(hivemq, mqttVersion, clientId); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, clientId); mqttCliShell.executeAsync(List.of("exit")).awaitStdOut("mqtt>"); mqttCliShell.executeAsync(disconnectCommand).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); - assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { - disconnectAssertion.setDisconnectedClient(clientId); - }); + assertDisconnectPacket( + HIVE_MQ.getDisconnectInformations().get(0), + disconnectAssertion -> disconnectAssertion.setDisconnectedClient(clientId)); } @ParameterizedTest @@ -150,9 +150,9 @@ void test_disconnectById(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_disconnectAll(final char mqttVersion) throws Exception { final List disconnectAllCommand = List.of("dis", "-a"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client2"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client3"); mqttCliShell.executeAsync(disconnectAllCommand) .awaitStdOut("mqtt>") @@ -160,9 +160,9 @@ void test_disconnectAll(final char mqttVersion) throws Exception { .awaitLog("sending DISCONNECT") .awaitLog("sending DISCONNECT"); - final String clientId1 = hivemq.getDisconnectInformations().get(0).getClientId(); - final String clientId2 = hivemq.getDisconnectInformations().get(1).getClientId(); - final String clientId3 = hivemq.getDisconnectInformations().get(2).getClientId(); + final String clientId1 = HIVE_MQ.getDisconnectInformations().get(0).getClientId(); + final String clientId2 = HIVE_MQ.getDisconnectInformations().get(1).getClientId(); + final String clientId3 = HIVE_MQ.getDisconnectInformations().get(2).getClientId(); final ArrayList clientIdPool = new ArrayList<>(); clientIdPool.add(clientId1); clientIdPool.add(clientId2); @@ -170,17 +170,17 @@ void test_disconnectAll(final char mqttVersion) throws Exception { assertTrue(clientIdPool.containsAll(List.of("client1", "client2", "client3"))); - assertDisconnectPacket(hivemq.getDisconnectInformations().get(0), disconnectAssertion -> { - disconnectAssertion.setDisconnectedClient(clientId1); - }); + assertDisconnectPacket( + HIVE_MQ.getDisconnectInformations().get(0), + disconnectAssertion -> disconnectAssertion.setDisconnectedClient(clientId1)); - assertDisconnectPacket(hivemq.getDisconnectInformations().get(1), disconnectAssertion -> { - disconnectAssertion.setDisconnectedClient(clientId2); - }); + assertDisconnectPacket( + HIVE_MQ.getDisconnectInformations().get(1), + disconnectAssertion -> disconnectAssertion.setDisconnectedClient(clientId2)); - assertDisconnectPacket(hivemq.getDisconnectInformations().get(2), disconnectAssertion -> { - disconnectAssertion.setDisconnectedClient(clientId3); - }); + assertDisconnectPacket( + HIVE_MQ.getDisconnectInformations().get(2), + disconnectAssertion -> disconnectAssertion.setDisconnectedClient(clientId3)); } @Test diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java index e987e5df7..43961e96a 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellExitST.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.shell; import com.hivemq.cli.utils.broker.HiveMQ; @@ -29,10 +30,10 @@ import static org.awaitility.Awaitility.await; -public class ShellExitST { +class ShellExitST { @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -43,9 +44,9 @@ public class ShellExitST { void test_exitContext(final char mqttVersion) throws Exception { final List exitCommand = List.of("exit"); final List lsCommand = List.of("ls"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client"); mqttCliShell.executeAsync(exitCommand).awaitStdOut("mqtt>"); - mqttCliShell.executeAsync(lsCommand).awaitStdOut(String.format("client@%s", hivemq.getHost())); + mqttCliShell.executeAsync(lsCommand).awaitStdOut(String.format("client@%s", HIVE_MQ.getHost())); } @Test diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java index 43e393a4c..8d8d70682 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellListST.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.shell; -import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -29,10 +30,10 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class ShellListST { +class ShellListST { @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -49,13 +50,13 @@ void test_emptyList() throws Exception { @ValueSource(chars = {'3', '5'}) void test_listConnectedClients(final char mqttVersion) throws Exception { final List listCommand = List.of("ls"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client2"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client3"); mqttCliShell.executeAsync(listCommand) - .awaitStdOut(String.format("client1@%s", hivemq.getHost())) - .awaitStdOut(String.format("client2@%s", hivemq.getHost())) - .awaitStdOut(String.format("client3@%s", hivemq.getHost())); + .awaitStdOut(String.format("client1@%s", HIVE_MQ.getHost())) + .awaitStdOut(String.format("client2@%s", HIVE_MQ.getHost())) + .awaitStdOut(String.format("client3@%s", HIVE_MQ.getHost())); } @ParameterizedTest @@ -63,13 +64,13 @@ void test_listConnectedClients(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_listConnectedClientsReverse(final char mqttVersion) throws Exception { final List listCommand = List.of("ls", "-r"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client2"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client3"); mqttCliShell.executeAsync(listCommand) - .awaitStdOut(String.format("client3@%s", hivemq.getHost())) - .awaitStdOut(String.format("client2@%s", hivemq.getHost())) - .awaitStdOut(String.format("client1@%s", hivemq.getHost())); + .awaitStdOut(String.format("client3@%s", HIVE_MQ.getHost())) + .awaitStdOut(String.format("client2@%s", HIVE_MQ.getHost())) + .awaitStdOut(String.format("client1@%s", HIVE_MQ.getHost())); } @ParameterizedTest @@ -77,29 +78,29 @@ void test_listConnectedClientsReverse(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_listLongConnectedClients(final char mqttVersion) throws Exception { final List listCommand = List.of("ls", "-l"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client3"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client2"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client3"); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(listCommand); awaitOutput.awaitStdOut("CONNECTED") .awaitStdOut("client1") - .awaitStdOut(hivemq.getHost()) - .awaitStdOut(String.valueOf(hivemq.getMqttPort())) + .awaitStdOut(HIVE_MQ.getHost()) + .awaitStdOut(String.valueOf(HIVE_MQ.getMqttPort())) .awaitStdOut(MqttVersionConverter.toClientVersion(mqttVersion).name()) .awaitStdOut("NO_SSL"); awaitOutput.awaitStdOut("CONNECTED") .awaitStdOut("client2") - .awaitStdOut(hivemq.getHost()) - .awaitStdOut(String.valueOf(hivemq.getMqttPort())) + .awaitStdOut(HIVE_MQ.getHost()) + .awaitStdOut(String.valueOf(HIVE_MQ.getMqttPort())) .awaitStdOut(MqttVersionConverter.toClientVersion(mqttVersion).name()) .awaitStdOut("NO_SSL"); awaitOutput.awaitStdOut("CONNECTED") .awaitStdOut("client3") - .awaitStdOut(hivemq.getHost()) - .awaitStdOut(String.valueOf(hivemq.getMqttPort())) + .awaitStdOut(HIVE_MQ.getHost()) + .awaitStdOut(String.valueOf(HIVE_MQ.getMqttPort())) .awaitStdOut(MqttVersionConverter.toClientVersion(mqttVersion).name()) .awaitStdOut("NO_SSL"); } @@ -110,20 +111,20 @@ void test_listLongConnectedClients(final char mqttVersion) throws Exception { void test_listSubscriptions(final char mqttVersion) throws Exception { final List listCommand = List.of("ls", "-s"); - mqttCliShell.connectClient(hivemq, mqttVersion, "subscription-client1"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "subscription-client1"); mqttCliShell.executeAsync(List.of("sub", "-t", "topic1")) - .awaitStdOut(String.format("subscription-client1@%s>", hivemq.getHost())) + .awaitStdOut(String.format("subscription-client1@%s>", HIVE_MQ.getHost())) .awaitLog("received SUBACK"); - mqttCliShell.connectClient(hivemq, mqttVersion, "subscription-client2"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "subscription-client2"); mqttCliShell.executeAsync(List.of("sub", "-t", "topic2")) - .awaitStdOut(String.format("subscription-client2@%s>", hivemq.getHost())) + .awaitStdOut(String.format("subscription-client2@%s>", HIVE_MQ.getHost())) .awaitLog("received SUBACK"); mqttCliShell.executeAsync(listCommand) - .awaitStdOut(String.format("subscription-client1@%s", hivemq.getHost())) + .awaitStdOut(String.format("subscription-client1@%s", HIVE_MQ.getHost())) .awaitStdOut("-subscribed topics: [topic1]") - .awaitStdOut(String.format("subscription-client2@%s", hivemq.getHost())) + .awaitStdOut(String.format("subscription-client2@%s", HIVE_MQ.getHost())) .awaitStdOut("-subscribed topics: [topic2]"); } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java index 1e5692301..93b5ea6d5 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellPublishST.java @@ -17,9 +17,9 @@ package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; import com.hivemq.extension.sdk.api.packets.publish.PublishPacket; @@ -37,16 +37,17 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import static com.hivemq.cli.utils.broker.assertions.PublishAssertion.assertPublishPacket; import static org.junit.jupiter.api.Assertions.assertTrue; -public class ShellPublishST { +class ShellPublishST { @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -56,13 +57,13 @@ public class ShellPublishST { @ValueSource(chars = {'3', '5'}) void test_successfulPublish(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending PUBLISH") .awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); }); @@ -75,13 +76,13 @@ void test_messageToFile(final char mqttVersion) throws Exception { final Path publishFile = Files.createTempFile("publish", "txt"); Files.write(publishFile, "message".getBytes(StandardCharsets.UTF_8)); final List publishCommand = List.of("pub", "-t", "test", "-m:file", publishFile.toString()); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending PUBLISH") .awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("message".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); }); @@ -92,29 +93,30 @@ void test_messageToFile(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_multipleTopics(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test1", "-t", "test2", "-t", "test3", "-m", "test"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending PUBLISH") .awaitLog("received PUBLISH acknowledgement"); - final PublishPacket publishPacket1 = hivemq.getPublishPackets().get(0); - final PublishPacket publishPacket2 = hivemq.getPublishPackets().get(1); - final PublishPacket publishPacket3 = hivemq.getPublishPackets().get(2); - final Set topicSet = Set.of(publishPacket1.getTopic(), publishPacket2.getTopic(), publishPacket3.getTopic()); + final PublishPacket publishPacket1 = HIVE_MQ.getPublishPackets().get(0); + final PublishPacket publishPacket2 = HIVE_MQ.getPublishPackets().get(1); + final PublishPacket publishPacket3 = HIVE_MQ.getPublishPackets().get(2); + final Set topicSet = + Set.of(publishPacket1.getTopic(), publishPacket2.getTopic(), publishPacket3.getTopic()); assertTrue(topicSet.containsAll(List.of("test1", "test2", "test3"))); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic(publishPacket1.getTopic()); }); - assertPublishPacket(hivemq.getPublishPackets().get(1), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(1), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic(publishPacket2.getTopic()); }); - assertPublishPacket(hivemq.getPublishPackets().get(2), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(2), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic(publishPacket3.getTopic()); }); @@ -125,13 +127,13 @@ void test_multipleTopics(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_qos(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-q", "1"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending PUBLISH") .awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); publishAssertion.setQos(Qos.AT_LEAST_ONCE); @@ -142,38 +144,41 @@ void test_qos(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_multipleTopicsAndMultipleQos(final char mqttVersion) throws Exception { - final List publishCommand = List.of( - "pub", - "-t", "test1", - "-t", "test2", - "-t", "test3", - "-q", "0", - "-q", "1", - "-q", "2", - "-m", "test" - ); - mqttCliShell.connectClient(hivemq, mqttVersion); + final List publishCommand = List.of("pub", + "-t", + "test1", + "-t", + "test2", + "-t", + "test3", + "-q", + "0", + "-q", + "1", + "-q", + "2", + "-m", + "test"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending PUBLISH") .awaitLog("received PUBLISH acknowledgement"); - final PublishPacket publishPacket1 = hivemq.getPublishPackets() - .stream() - .filter(publish -> publish.getTopic().equals("test1")) - .findFirst() - .get(); - final PublishPacket publishPacket2 = hivemq.getPublishPackets() - .stream() - .filter(publish -> publish.getTopic().equals("test2")) - .findFirst() - .get(); - final PublishPacket publishPacket3 = hivemq.getPublishPackets() - .stream() - .filter(publish -> publish.getTopic().equals("test3")) - .findFirst() - .get(); - final Set topicSet = Set.of(publishPacket1.getTopic(), publishPacket2.getTopic(), publishPacket3.getTopic()); + final Optional optionalPublishPacket1 = + HIVE_MQ.getPublishPackets().stream().filter(publish -> publish.getTopic().equals("test1")).findFirst(); + assertTrue(optionalPublishPacket1.isPresent()); + final PublishPacket publishPacket1 = optionalPublishPacket1.get(); + final Optional optionalPublishPacket2 = + HIVE_MQ.getPublishPackets().stream().filter(publish -> publish.getTopic().equals("test2")).findFirst(); + assertTrue(optionalPublishPacket2.isPresent()); + final PublishPacket publishPacket2 = optionalPublishPacket2.get(); + final Optional optionalPublishPacket3 = + HIVE_MQ.getPublishPackets().stream().filter(publish -> publish.getTopic().equals("test3")).findFirst(); + assertTrue(optionalPublishPacket3.isPresent()); + final PublishPacket publishPacket3 = optionalPublishPacket3.get(); + final Set topicSet = + Set.of(publishPacket1.getTopic(), publishPacket2.getTopic(), publishPacket3.getTopic()); assertTrue(topicSet.containsAll(List.of("test1", "test2", "test3"))); assertPublishPacket(publishPacket1, publishAssertion -> { @@ -200,13 +205,13 @@ void test_multipleTopicsAndMultipleQos(final char mqttVersion) throws Exception @ValueSource(chars = {'3', '5'}) void test_retain(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-r"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending PUBLISH") .awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); publishAssertion.setRetain(true); @@ -218,23 +223,23 @@ void test_retain(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_messageExpiryInterval(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-e", "120"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Publish message expiry was set but is unused in MQTT Version MQTT_3_1_1"); awaitOutput.awaitLog("Publish message expiry was set but is unused in MQTT Version MQTT_3_1_1"); - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending PUBLISH"); awaitOutput.awaitLog("received PUBLISH acknowledgement"); } else { - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending PUBLISH"); awaitOutput.awaitLog("received PUBLISH acknowledgement"); awaitOutput.awaitLog("messageExpiryInterval=120"); } - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); if (mqttVersion == '5') { @@ -248,7 +253,7 @@ void test_messageExpiryInterval(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_payloadFormatIndicator(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-pf", "utf8"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); if (mqttVersion == '3') { @@ -256,11 +261,11 @@ void test_payloadFormatIndicator(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Publish payload format indicator was set but is unused in MQTT Version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending PUBLISH"); awaitOutput.awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); if (mqttVersion == '5') { @@ -274,7 +279,7 @@ void test_payloadFormatIndicator(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_contentType(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-ct", "my-content"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); if (mqttVersion == '3') { @@ -282,11 +287,11 @@ void test_contentType(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Publish content type was set but is unused in MQTT Version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending PUBLISH"); awaitOutput.awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); if (mqttVersion == '5') { @@ -300,7 +305,7 @@ void test_contentType(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_responseTopic(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-rt", "response-topic"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); if (mqttVersion == '3') { @@ -308,11 +313,11 @@ void test_responseTopic(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Publish response topic was set but is unused in MQTT Version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending PUBLISH"); awaitOutput.awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); if (mqttVersion == '5') { @@ -326,7 +331,7 @@ void test_responseTopic(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_correlationData(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-cd", "correlation-data"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); if (mqttVersion == '3') { @@ -334,11 +339,11 @@ void test_correlationData(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Publish correlation data was set but is unused in MQTT Version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending PUBLISH"); awaitOutput.awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); if (mqttVersion == '5') { @@ -351,8 +356,9 @@ void test_correlationData(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_userProperties(final char mqttVersion) throws Exception { - final List publishCommand = List.of("pub", "-t", "test", "-m", "test", "-up", "key1=value1", "-up", "key2=value2"); - mqttCliShell.connectClient(hivemq, mqttVersion); + final List publishCommand = + List.of("pub", "-t", "test", "-m", "test", "-up", "key1=value1", "-up", "key2=value2"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(publishCommand); if (mqttVersion == '3') { @@ -360,18 +366,19 @@ void test_userProperties(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Publish user properties were set but is unused in MQTT Version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending PUBLISH"); awaitOutput.awaitLog("received PUBLISH acknowledgement"); - assertPublishPacket(hivemq.getPublishPackets().get(0), publishAssertion -> { + assertPublishPacket(HIVE_MQ.getPublishPackets().get(0), publishAssertion -> { publishAssertion.setPayload(ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8))); publishAssertion.setTopic("test"); if (mqttVersion == '5') { - final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.builder() - .add(new MqttUserProperty("key1", "value1")) - .add(new MqttUserProperty("key2", "value2")) - .build()); + final UserPropertiesImpl expectedUserProperties = + UserPropertiesImpl.of(ImmutableList.builder() + .add(new MqttUserProperty("key1", "value1")) + .add(new MqttUserProperty("key2", "value2")) + .build()); publishAssertion.setUserProperties(expectedUserProperties); } }); @@ -382,10 +389,10 @@ void test_userProperties(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_publishMissingTopic(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Missing required option: '--topic '") - .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); + .awaitStdOut("cliTest@" + HIVE_MQ.getHost() + ">"); } @ParameterizedTest @@ -393,10 +400,10 @@ void test_publishMissingTopic(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_publishMissingMessage(final char mqttVersion) throws Exception { final List publishCommand = List.of("pub", "-t", "test"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(publishCommand) .awaitStdErr("Error: Missing required argument (specify one of these)") - .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); + .awaitStdOut("cliTest@" + HIVE_MQ.getHost() + ">"); } @Test diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java index 09b60a02a..24a010529 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSubscribeST.java @@ -18,9 +18,9 @@ import com.google.common.collect.ImmutableList; import com.google.gson.JsonObject; -import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.client.mqtt.MqttClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.extension.sdk.api.packets.general.Qos; @@ -46,10 +46,10 @@ import static com.hivemq.cli.utils.broker.assertions.SubscribeAssertion.assertSubscribePacket; import static org.junit.jupiter.api.Assertions.assertEquals; -public class ShellSubscribeST { +class ShellSubscribeST { @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -59,31 +59,29 @@ public class ShellSubscribeST { @ValueSource(chars = {'3', '5'}) void test_successfulSubscribe(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending SUBSCRIBE") .awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", - Qos.EXACTLY_ONCE, - RetainHandling.SEND, - false, - false))); - }); + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), + subscribeAssertion -> subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)))); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_multipleTopics(final char mqttVersion) throws Exception { - //FIXME Subscribe command should susbscribe to all topics in one packet and not send separate subscribes + //FIXME Subscribe command should subscribe to all topics in one packet and not send separate subscribes final List subscribeCommand = List.of("sub", "-t", "test1", "-t", "test2", "-t", "test3"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending SUBSCRIBE") .awaitLog("received SUBACK") .awaitLog("sending SUBSCRIBE") @@ -91,19 +89,19 @@ void test_multipleTopics(final char mqttVersion) throws Exception { .awaitLog("sending SUBSCRIBE") .awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("test1", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(1), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("test2", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(2), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("test3", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); @@ -114,12 +112,12 @@ void test_multipleTopics(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { - //FIXME Subscribe command should susbscribe to all topics in one packet and not send separate subscribes + //FIXME Subscribe command should subscribe to all topics in one packet and not send separate subscribes final List subscribeCommand = List.of("sub", "-t", "test1", "-t", "test2", "-t", "test3", "-q", "0", "-q", "1", "-q", "2"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending SUBSCRIBE") .awaitLog("received SUBACK") .awaitLog("sending SUBSCRIBE") @@ -127,19 +125,19 @@ void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { .awaitLog("sending SUBSCRIBE") .awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("test1", Qos.AT_MOST_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(1), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(1), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("test2", Qos.AT_LEAST_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); }); - assertSubscribePacket(hivemq.getSubscribePackets().get(2), subscribeAssertion -> { + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(2), subscribeAssertion -> { final List expectedSubscriptions = List.of(new SubscriptionImpl("test3", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, false)); subscribeAssertion.setSubscriptions(expectedSubscriptions); @@ -151,7 +149,7 @@ void test_multipleTopicsMultipleQos(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_userProperties(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test", "-up", "key1=value1", "-up", "key2=value2"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand); if (mqttVersion == '3') { @@ -159,24 +157,21 @@ void test_userProperties(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Subscribe user properties were set but are unused in MQTT version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending SUBSCRIBE"); awaitOutput.awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), subscribeAssertion -> { + subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", Qos.EXACTLY_ONCE, RetainHandling.SEND, false, - false)) - ); + false))); if (mqttVersion == '5') { - final UserPropertiesImpl userProperties = UserPropertiesImpl.of(ImmutableList.of( - MqttUserProperty.of("key1", "value1"), - MqttUserProperty.of("key2", "value2")) - ); + final UserPropertiesImpl userProperties = + UserPropertiesImpl.of(ImmutableList.of(MqttUserProperty.of("key1", "value1"), + MqttUserProperty.of("key2", "value2"))); subscribeAssertion.setUserProperties(userProperties); } }); @@ -187,23 +182,20 @@ void test_userProperties(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_stay(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test", "-s"); - mqttCliShell.connectClient(hivemq, mqttVersion); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) - .awaitLog("sending SUBSCRIBE") - .awaitLog("received SUBACK"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(subscribeCommand).awaitLog("sending SUBSCRIBE").awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", - Qos.EXACTLY_ONCE, - RetainHandling.SEND, - false, - false))); - }); + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), + subscribeAssertion -> subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)))); final Mqtt5BlockingClient publisher = MqttClient.builder() - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) + .serverHost(HIVE_MQ.getHost()) + .serverPort(HIVE_MQ.getMqttPort()) .useMqttVersion5() .buildBlocking(); publisher.connect(); @@ -218,23 +210,20 @@ void test_stay(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_outputToConsole(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test", "-oc"); - mqttCliShell.connectClient(hivemq, mqttVersion); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) - .awaitLog("sending SUBSCRIBE") - .awaitLog("received SUBACK"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(subscribeCommand).awaitLog("sending SUBSCRIBE").awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", - Qos.EXACTLY_ONCE, - RetainHandling.SEND, - false, - false))); - }); + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), + subscribeAssertion -> subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)))); final Mqtt5BlockingClient publisher = MqttClient.builder() - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) + .serverHost(HIVE_MQ.getHost()) + .serverPort(HIVE_MQ.getMqttPort()) .useMqttVersion5() .buildBlocking(); publisher.connect(); @@ -251,23 +240,20 @@ void test_outputToFile(final char mqttVersion) throws Exception { final Path outputFile = Files.createTempFile("publishes", ".txt"); outputFile.toFile().deleteOnExit(); final List subscribeCommand = List.of("sub", "-t", "test", "-of", outputFile.toString()); - mqttCliShell.connectClient(hivemq, mqttVersion); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) - .awaitLog("sending SUBSCRIBE") - .awaitLog("received SUBACK"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(subscribeCommand).awaitLog("sending SUBSCRIBE").awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", - Qos.EXACTLY_ONCE, - RetainHandling.SEND, - false, - false))); - }); + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), + subscribeAssertion -> subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)))); final Mqtt5BlockingClient publisher = MqttClient.builder() - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) + .serverHost(HIVE_MQ.getHost()) + .serverPort(HIVE_MQ.getMqttPort()) .useMqttVersion5() .buildBlocking(); publisher.connect(); @@ -285,23 +271,20 @@ void test_outputToFile(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_base64(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test", "-s", "-b64"); - mqttCliShell.connectClient(hivemq, mqttVersion); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) - .awaitLog("sending SUBSCRIBE") - .awaitLog("received SUBACK"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(subscribeCommand).awaitLog("sending SUBSCRIBE").awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", - Qos.EXACTLY_ONCE, - RetainHandling.SEND, - false, - false))); - }); + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), + subscribeAssertion -> subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)))); final Mqtt5BlockingClient publisher = MqttClient.builder() - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) + .serverHost(HIVE_MQ.getHost()) + .serverPort(HIVE_MQ.getMqttPort()) .useMqttVersion5() .buildBlocking(); publisher.connect(); @@ -317,23 +300,20 @@ void test_base64(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_showTopics(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test", "-s", "-T"); - mqttCliShell.connectClient(hivemq, mqttVersion); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) - .awaitLog("sending SUBSCRIBE") - .awaitLog("received SUBACK"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(subscribeCommand).awaitLog("sending SUBSCRIBE").awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", - Qos.EXACTLY_ONCE, - RetainHandling.SEND, - false, - false))); - }); + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), + subscribeAssertion -> subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)))); final Mqtt5BlockingClient publisher = MqttClient.builder() - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) + .serverHost(HIVE_MQ.getHost()) + .serverPort(HIVE_MQ.getMqttPort()) .useMqttVersion5() .buildBlocking(); publisher.connect(); @@ -348,23 +328,20 @@ void test_showTopics(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_Json(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub", "-t", "test", "-s", "-J"); - mqttCliShell.connectClient(hivemq, mqttVersion); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand) - .awaitLog("sending SUBSCRIBE") - .awaitLog("received SUBACK"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(subscribeCommand).awaitLog("sending SUBSCRIBE").awaitLog("received SUBACK"); - assertSubscribePacket(hivemq.getSubscribePackets().get(0), subscribeAssertion -> { - subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl( - "test", - Qos.EXACTLY_ONCE, - RetainHandling.SEND, - false, - false))); - }); + assertSubscribePacket(HIVE_MQ.getSubscribePackets().get(0), + subscribeAssertion -> subscribeAssertion.setSubscriptions(List.of(new SubscriptionImpl("test", + Qos.EXACTLY_ONCE, + RetainHandling.SEND, + false, + false)))); final Mqtt5BlockingClient publisher = MqttClient.builder() - .serverHost(hivemq.getHost()) - .serverPort(hivemq.getMqttPort()) + .serverHost(HIVE_MQ.getHost()) + .serverPort(HIVE_MQ.getMqttPort()) .useMqttVersion5() .buildBlocking(); publisher.connect(); @@ -375,13 +352,9 @@ void test_Json(final char mqttVersion) throws Exception { publisher.publishWith().topic("test").payload(jsonObject.toString().getBytes(StandardCharsets.UTF_8)).send(); - awaitOutput.awaitStdOut("{\n" + - " \"topic\": \"test\",\n" + - " \"payload\": {\n" + - " \"property1\": \"value1\",\n" + - " \"property2\": \"value2\",\n" + - " \"property3\": \"value3\"\n" + - " },\n"); + awaitOutput.awaitStdOut( + "{\n" + " \"topic\": \"test\",\n" + " \"payload\": {\n" + " \"property1\": \"value1\",\n" + + " \"property2\": \"value2\",\n" + " \"property3\": \"value3\"\n" + " },\n"); awaitOutput.awaitStdOut("\"qos\": \"AT_MOST_ONCE\","); awaitOutput.awaitStdOut("\"receivedAt\":"); awaitOutput.awaitStdOut("\"retain\": false"); @@ -394,10 +367,10 @@ void test_Json(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_subscribeMissingTopic(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("sub"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) .awaitStdErr("Missing required option: '--topic '") - .awaitStdOut("cliTest@" + hivemq.getHost() + ">"); + .awaitStdOut("cliTest@" + HIVE_MQ.getHost() + ">"); } @Test diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java index 514c22c34..1f660269c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellSwitchST.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.shell; import com.hivemq.cli.utils.broker.HiveMQ; @@ -26,9 +27,10 @@ import java.util.List; import java.util.concurrent.TimeUnit; -public class ShellSwitchST { +class ShellSwitchST { + @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -37,39 +39,39 @@ public class ShellSwitchST { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_successfulSwitchFromContext(final char mqttVersion) throws Exception { - final List switchCommand = List.of("switch", String.format("client1@%s", hivemq.getHost())); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); - mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + final List switchCommand = List.of("switch", String.format("client1@%s", HIVE_MQ.getHost())); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client2"); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", HIVE_MQ.getHost())); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_successfulSwitchWithoutContext(final char mqttVersion) throws Exception { - final List switchCommand = List.of("switch", String.format("client1@%s", hivemq.getHost())); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + final List switchCommand = List.of("switch", String.format("client1@%s", HIVE_MQ.getHost())); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); mqttCliShell.executeAsync(List.of("exit")).awaitStdOut("mqtt>"); - mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", HIVE_MQ.getHost())); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_hostAndIdentifierWithContext(final char mqttVersion) throws Exception { - final List switchCommand = List.of("switch", "-i", "client1", "-h", hivemq.getHost()); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); - mqttCliShell.connectClient(hivemq, mqttVersion, "client2"); - mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + final List switchCommand = List.of("switch", "-i", "client1", "-h", HIVE_MQ.getHost()); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client2"); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", HIVE_MQ.getHost())); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_hostAndIdentifierWithoutContext(final char mqttVersion) throws Exception { - final List switchCommand = List.of("switch", "-i", "client1", "-h", hivemq.getHost()); - mqttCliShell.connectClient(hivemq, mqttVersion, "client1"); + final List switchCommand = List.of("switch", "-i", "client1", "-h", HIVE_MQ.getHost()); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion, "client1"); mqttCliShell.executeAsync(List.of("exit")).awaitStdOut("mqtt>"); - mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(switchCommand).awaitStdOut(String.format("client1@%s>", HIVE_MQ.getHost())); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java index f8b85d526..f973b7185 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/ShellUnsubscribeST.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.shell; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; @@ -34,10 +35,10 @@ import static com.hivemq.cli.utils.broker.assertions.UnsubscribeAssertion.assertUnsubscribePacket; import static org.junit.jupiter.api.Assertions.assertEquals; -public class ShellUnsubscribeST { +class ShellUnsubscribeST { @RegisterExtension - private static final @NotNull HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @@ -47,15 +48,15 @@ public class ShellUnsubscribeST { @ValueSource(chars = {'3', '5'}) void test_successfulUnsubscribe(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("unsub", "-t", "test"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending UNSUBSCRIBE") .awaitLog("received UNSUBACK"); - assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(0), unsubscribeAssertion -> { - unsubscribeAssertion.setTopicFilters(List.of("test")); - }); + assertUnsubscribePacket( + HIVE_MQ.getUnsubscribePackets().get(0), + unsubscribeAssertion -> unsubscribeAssertion.setTopicFilters(List.of("test"))); } @ParameterizedTest @@ -63,9 +64,9 @@ void test_successfulUnsubscribe(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_multipleTopics(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("unsub", "-t", "test1", "-t", "test2", "-t", "test3"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending UNSUBSCRIBE") .awaitLog("received UNSUBACK") .awaitLog("sending UNSUBSCRIBE") @@ -74,43 +75,45 @@ void test_multipleTopics(final char mqttVersion) throws Exception { .awaitLog("received UNSUBACK"); - assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(0), unsubscribeAssertion -> { - unsubscribeAssertion.setTopicFilters(List.of("test1")); - }); + assertUnsubscribePacket( + HIVE_MQ.getUnsubscribePackets().get(0), + unsubscribeAssertion -> unsubscribeAssertion.setTopicFilters(List.of("test1"))); - assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(1), unsubscribeAssertion -> { - unsubscribeAssertion.setTopicFilters(List.of("test2")); - }); + assertUnsubscribePacket( + HIVE_MQ.getUnsubscribePackets().get(1), + unsubscribeAssertion -> unsubscribeAssertion.setTopicFilters(List.of("test2"))); - assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(2), unsubscribeAssertion -> { - unsubscribeAssertion.setTopicFilters(List.of("test3")); - }); + assertUnsubscribePacket( + HIVE_MQ.getUnsubscribePackets().get(2), + unsubscribeAssertion -> unsubscribeAssertion.setTopicFilters(List.of("test3"))); } @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_userProperties(final char mqttVersion) throws Exception { - final List subscribeCommand = List.of("unsub", "-t", "test", "-up", "key1=value1", "-up", "key2=value2"); - mqttCliShell.connectClient(hivemq, mqttVersion); + final List subscribeCommand = + List.of("unsub", "-t", "test", "-up", "key1=value1", "-up", "key2=value2"); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(subscribeCommand); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Unsubscribe user properties were set but are unused in MQTT Version MQTT_3_1_1"); awaitOutput.awaitLog("Unsubscribe user properties were set but are unused in MQTT Version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending UNSUBSCRIBE"); awaitOutput.awaitLog("received UNSUBACK"); - assertUnsubscribePacket(hivemq.getUnsubscribePackets().get(0), unsubscribeAssertion -> { + assertUnsubscribePacket(HIVE_MQ.getUnsubscribePackets().get(0), unsubscribeAssertion -> { unsubscribeAssertion.setTopicFilters(List.of("test")); if (mqttVersion == '5') { - final UserPropertiesImpl expectedUserProperties = UserPropertiesImpl.of(ImmutableList.builder() - .add(MqttUserProperty.of("key1", "value1")) - .add(MqttUserProperty.of("key2", "value2")) - .build()); + final UserPropertiesImpl expectedUserProperties = + UserPropertiesImpl.of(ImmutableList.builder() + .add(MqttUserProperty.of("key1", "value1")) + .add(MqttUserProperty.of("key2", "value2")) + .build()); unsubscribeAssertion.setUserProperties(expectedUserProperties); } }); @@ -121,13 +124,13 @@ void test_userProperties(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_missingTopic(final char mqttVersion) throws Exception { final List subscribeCommand = List.of("unsub"); - mqttCliShell.connectClient(hivemq, mqttVersion); + mqttCliShell.connectClient(HIVE_MQ, mqttVersion); mqttCliShell.executeAsync(subscribeCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitStdErr("Missing required option: '--topic '") .awaitStdErr("Try 'help unsub' for more information."); - assertEquals(0, hivemq.getUnsubscribePackets().size()); + assertEquals(0, HIVE_MQ.getUnsubscribePackets().size()); } @Test @@ -138,6 +141,6 @@ void test_UnsubscribeWhileNotConnected() throws Exception { .awaitStdErr("Unmatched argument at index 0: 'unsub'") .awaitStdErr("Try 'help' to get a list of commands."); - assertEquals(0, hivemq.getUnsubscribePackets().size()); + assertEquals(0, HIVE_MQ.getUnsubscribePackets().size()); } } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java index 99b847b54..659aca7e8 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectEnvST.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.shell.connect; -import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; @@ -34,15 +35,15 @@ import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; -public class ShellConnectEnvST { +class ShellConnectEnvST { - private static final String PASSWORD_ENV = "PASSWORD"; + private static final @NotNull String PASSWORD_ENV = "PASSWORD"; @RegisterExtension - private final static HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension - final MqttCliShell mqttCliShell = new MqttCliShell(Map.of(PASSWORD_ENV, "password")); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(Map.of(PASSWORD_ENV, "password")); @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @@ -59,10 +60,10 @@ void test_passwordEnv(final char mqttVersion) throws Exception { awaitOutput.awaitStdOut("mqtt>"); awaitOutput.awaitLog("Password-Only Authentication is not allowed in MQTT 3"); } else { - awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + awaitOutput.awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -81,10 +82,10 @@ void test_userNameAndPasswordEnv(final char mqttVersion) throws Exception { final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); - awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + awaitOutput.awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("user"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -96,9 +97,9 @@ void test_userNameAndPasswordEnv(final char mqttVersion) throws Exception { final ArrayList defaultConnectCommand = new ArrayList<>(); defaultConnectCommand.add("con"); defaultConnectCommand.add("-h"); - defaultConnectCommand.add(hivemq.getHost()); + defaultConnectCommand.add(HIVE_MQ.getHost()); defaultConnectCommand.add("-p"); - defaultConnectCommand.add(String.valueOf(hivemq.getMqttPort())); + defaultConnectCommand.add(String.valueOf(HIVE_MQ.getMqttPort())); defaultConnectCommand.add("-i"); defaultConnectCommand.add("cliTest"); return defaultConnectCommand; diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index 566b8bd89..3b60e8f7c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -17,10 +17,10 @@ package com.hivemq.cli.commands.shell.connect; import com.google.common.collect.ImmutableList; -import com.hivemq.cli.utils.cli.results.AwaitOutput; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; -import com.hivemq.cli.utils.MqttVersionConverter; +import com.hivemq.cli.utils.cli.results.AwaitOutput; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.general.Qos; import com.hivemq.extension.sdk.api.packets.publish.PayloadFormatIndicator; @@ -47,13 +47,13 @@ import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.assertEquals; -public class ShellConnectST { +class ShellConnectST { @RegisterExtension - private final static HiveMQ hivemq = HiveMQ.builder().build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().build(); @RegisterExtension - final MqttCliShell mqttCliShell = new MqttCliShell(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) @@ -69,12 +69,15 @@ void test_defaultConnect(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); - assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion))); + final ConnectPacket connectPacket = HIVE_MQ.getConnectPackets().get(0); + assertConnectPacket( + connectPacket, + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); } @ParameterizedTest @@ -84,9 +87,9 @@ void test_connectWhileConnected(final char mqttVersion) throws Exception { final List connectCommand1 = List.of( "con", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-i", @@ -95,31 +98,31 @@ void test_connectWhileConnected(final char mqttVersion) throws Exception { final List connectCommand2 = List.of( "con", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-i", "client2"); mqttCliShell.executeAsync(connectCommand1) - .awaitStdOut(String.format("client1@%s>", hivemq.getHost())) + .awaitStdOut(String.format("client1@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); + final ConnectPacket connectPacket1 = HIVE_MQ.getConnectPackets().get(0); assertConnectPacket(connectPacket1, connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId("client1"); }); mqttCliShell.executeAsync(connectCommand2) - .awaitStdOut(String.format("client2@%s>", hivemq.getHost())) + .awaitStdOut(String.format("client2@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); + final ConnectPacket connectPacket2 = HIVE_MQ.getConnectPackets().get(1); assertConnectPacket(connectPacket2, connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setClientId("client2"); @@ -131,14 +134,14 @@ void test_connectWhileConnected(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_wrongPort(final char mqttVersion) throws Exception { final List connectCommand = - List.of("con", "-h", hivemq.getHost(), "-p", "22", "-V", String.valueOf(mqttVersion), "-i", "cliTest"); + List.of("con", "-h", HIVE_MQ.getHost(), "-p", "22", "-V", String.valueOf(mqttVersion), "-i", "cliTest"); mqttCliShell.executeAsync(connectCommand) .awaitStdErr("Connection refused") .awaitStdOut("mqtt>") .awaitLog("Connection refused"); - assertEquals(0, hivemq.getConnectPackets().size()); + assertEquals(0, HIVE_MQ.getConnectPackets().size()); } @ParameterizedTest @@ -150,7 +153,7 @@ void test_wrongHost(final char mqttVersion) throws Exception { "-h", "unreachable-host", "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-i", @@ -161,7 +164,7 @@ void test_wrongHost(final char mqttVersion) throws Exception { .awaitStdOut("mqtt>") .awaitLog("nodename nor servname provided, or not known"); - assertEquals(0, hivemq.getConnectPackets().size()); + assertEquals(0, HIVE_MQ.getConnectPackets().size()); } @ParameterizedTest @@ -172,11 +175,11 @@ void test_cleanStart(final char mqttVersion) throws Exception { connectCommand.add("--no-cleanStart"); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '3') { connectAssertion.setSessionExpiryInterval(4294967295L); @@ -194,7 +197,7 @@ void test_receiveMaximum(final char mqttVersion) throws Exception { connectCommand.add("500"); final AwaitOutput awaitOutput = - mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Restriction receive maximum was set but is unused in MQTT Version MQTT_3_1_1"); @@ -204,7 +207,7 @@ void test_receiveMaximum(final char mqttVersion) throws Exception { awaitOutput.awaitLog("sending CONNECT"); awaitOutput.awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setReceiveMaximum(500); @@ -221,7 +224,7 @@ void test_maxPacketSize(final char mqttVersion) throws Exception { connectCommand.add("500"); final AwaitOutput awaitOutput = - mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Restriction maximum packet size was set but is unused in MQTT Version MQTT_3_1_1"); @@ -231,7 +234,7 @@ void test_maxPacketSize(final char mqttVersion) throws Exception { awaitOutput.awaitLog("sending CONNECT"); awaitOutput.awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setMaximumPacketSize(500); @@ -248,7 +251,7 @@ void test_topicAliasMaximum(final char mqttVersion) throws Exception { connectCommand.add("5"); final AwaitOutput awaitOutput = - mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr("Restriction topic alias maximum was set but is unused in MQTT Version MQTT_3_1_1"); @@ -258,7 +261,7 @@ void test_topicAliasMaximum(final char mqttVersion) throws Exception { awaitOutput.awaitLog("sending CONNECT"); awaitOutput.awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setTopicAliasMaximum(5); @@ -274,7 +277,7 @@ void test_requestProblemInformation(final char mqttVersion) throws IOException { connectCommand.add("--no-reqProblemInfo"); final AwaitOutput awaitOutput = - mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr( @@ -286,7 +289,7 @@ void test_requestProblemInformation(final char mqttVersion) throws IOException { awaitOutput.awaitLog("sending CONNECT"); awaitOutput.awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestProblemInformation(false); @@ -302,7 +305,7 @@ void test_requestResponseInformation(final char mqttVersion) throws IOException connectCommand.add("--reqResponseInfo"); final AwaitOutput awaitOutput = - mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", hivemq.getHost())); + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); if (mqttVersion == '3') { awaitOutput.awaitStdErr( @@ -314,7 +317,7 @@ void test_requestResponseInformation(final char mqttVersion) throws IOException awaitOutput.awaitLog("sending CONNECT"); awaitOutput.awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { connectAssertion.setRequestResponseInformation(true); @@ -329,15 +332,14 @@ void test_noClientId(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-V", String.valueOf(mqttVersion)); - final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); @@ -345,7 +347,7 @@ void test_noClientId(final char mqttVersion) throws Exception { awaitOutput.awaitLog("assignedClientIdentifier="); } - assertEquals(1, hivemq.getConnectPackets().size()); + assertEquals(1, HIVE_MQ.getConnectPackets().size()); } @ParameterizedTest @@ -357,11 +359,11 @@ void test_userName(final char mqttVersion) throws Exception { connectCommand.add("username"); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("username"); }); @@ -382,10 +384,10 @@ void test_password(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Password-Only Authentication is not allowed in MQTT 3"); awaitOutput.awaitStdOut("mqtt>"); } else { - awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + awaitOutput.awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -403,10 +405,10 @@ void test_userNameAndPassword(final char mqttVersion) throws Exception { connectCommand.add("password"); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("user"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -417,7 +419,6 @@ void test_userNameAndPassword(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_passwordFile(final char mqttVersion) throws Exception { - final Path passwordFile = Files.createTempFile("mqtt-cli-password", ".txt"); passwordFile.toFile().deleteOnExit(); Files.writeString(passwordFile, "password", StandardCharsets.UTF_8); @@ -433,10 +434,10 @@ void test_passwordFile(final char mqttVersion) throws Exception { awaitOutput.awaitLog("Password-Only Authentication is not allowed in MQTT 3"); awaitOutput.awaitStdOut("mqtt>"); } else { - awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + awaitOutput.awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); }); @@ -459,10 +460,10 @@ void test_userNameAndPasswordFile(final char mqttVersion) throws Exception { connectCommand.add(passwordFile.toString()); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("@%s>", hivemq.getHost())) + .awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setUserName("user"); connectAssertion.setPassword(ByteBuffer.wrap("password".getBytes(StandardCharsets.UTF_8))); @@ -513,7 +514,7 @@ void test_will(final char mqttVersion) throws Exception { awaitOutput.awaitStdErr("Will Response Topic was set but is unused in MQTT Version MQTT_3_1_1"); awaitOutput.awaitStdErr("Will User Properties was set but is unused in MQTT Version MQTT_3_1_1"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { final WillPublishBuilder expectedWillBuilder = Builders.willPublish() .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) .topic("test-will-topic") @@ -524,13 +525,12 @@ void test_will(final char mqttVersion) throws Exception { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setWillPublish(expectedWillBuilder.build()); }); - } else { - awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())) + awaitOutput.awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { final WillPublishBuilder expectedWillBuilder = Builders.willPublish() .payload(ByteBuffer.wrap("will-message".getBytes(StandardCharsets.UTF_8))) .topic("test-will-topic") @@ -549,7 +549,6 @@ void test_will(final char mqttVersion) throws Exception { connectAssertion.setWillPublish(expectedWillBuilder.build()); }); } - } @ParameterizedTest @@ -560,9 +559,9 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-se", @@ -573,27 +572,26 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { if (mqttVersion == '3') { mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())) + .awaitStdOut(String.format("%s@%s>", clientId, HIVE_MQ.getHost())) .awaitStdErr("Connect session expiry interval was set but is unused in MQTT Version MQTT_3_1_1") .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setSessionExpiryInterval(4294967295L); connectAssertion.setCleanStart(false); connectAssertion.setClientId(clientId); }); - } else { mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())) + .awaitStdOut(String.format("%s@%s>", clientId, HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("sessionExpiryInterval=120") .awaitLog("received CONNACK") .awaitLog("sessionPresent=false"); - final ConnectPacket connectPacket1 = hivemq.getConnectPackets().get(0); + final ConnectPacket connectPacket1 = HIVE_MQ.getConnectPackets().get(0); assertConnectPacket(connectPacket1, connectAssertion -> { connectAssertion.setClientId(clientId); connectAssertion.setCleanStart(false); @@ -603,13 +601,13 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { mqttCliShell.executeAsync(List.of("dis")).awaitStdOut("mqtt>").awaitLog("sending DISCONNECT"); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())) + .awaitStdOut(String.format("%s@%s>", clientId, HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("sessionExpiryInterval=120") .awaitLog("received CONNACK") .awaitLog("sessionPresent=true"); - final ConnectPacket connectPacket2 = hivemq.getConnectPackets().get(1); + final ConnectPacket connectPacket2 = HIVE_MQ.getConnectPackets().get(1); assertConnectPacket(connectPacket2, connectAssertion -> { connectAssertion.setClientId(clientId); connectAssertion.setCleanStart(false); @@ -625,9 +623,9 @@ void test_identifierPrefix(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-V", String.valueOf(mqttVersion), "-ip", @@ -639,10 +637,9 @@ void test_identifierPrefix(final char mqttVersion) throws Exception { awaitOutput.awaitStdOut("myPrefix"); } - awaitOutput.awaitStdOut(String.format("@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending CONNECT"); awaitOutput.awaitLog("received CONNACK"); - } @ParameterizedTest @@ -661,7 +658,7 @@ void test_userProperties(final char mqttVersion) throws Exception { awaitOutput.awaitStdErr("Connect user properties were set but are unused in MQTT Version MQTT_3_1_1"); } - awaitOutput.awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())); + awaitOutput.awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())); awaitOutput.awaitLog("sending CONNECT"); if (mqttVersion == '5') { @@ -670,7 +667,7 @@ void test_userProperties(final char mqttVersion) throws Exception { awaitOutput.awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { final ImmutableList userProperties = ImmutableList.builder() @@ -680,7 +677,6 @@ void test_userProperties(final char mqttVersion) throws Exception { connectAssertion.setUserProperties(UserPropertiesImpl.of(userProperties)); } }); - } @ParameterizedTest @@ -690,9 +686,9 @@ void test_keepAlive(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", "-h", - hivemq.getHost(), + HIVE_MQ.getHost(), "-p", - String.valueOf(hivemq.getMqttPort()), + String.valueOf(HIVE_MQ.getMqttPort()), "-i", "cliTest", "-V", @@ -701,12 +697,12 @@ void test_keepAlive(final char mqttVersion) throws Exception { "30"); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("keepAlive=30") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); connectAssertion.setKeepAlive(30); }); @@ -716,9 +712,9 @@ void test_keepAlive(final char mqttVersion) throws Exception { final ArrayList defaultConnectCommand = new ArrayList<>(); defaultConnectCommand.add("con"); defaultConnectCommand.add("-h"); - defaultConnectCommand.add(hivemq.getHost()); + defaultConnectCommand.add(HIVE_MQ.getHost()); defaultConnectCommand.add("-p"); - defaultConnectCommand.add(String.valueOf(hivemq.getMqttPort())); + defaultConnectCommand.add(String.valueOf(HIVE_MQ.getMqttPort())); defaultConnectCommand.add("-i"); defaultConnectCommand.add("cliTest"); return defaultConnectCommand; diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java index 3f4d65aa9..57ce49fca 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectTlsST.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.shell.connect; import com.google.common.io.Resources; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; -import com.hivemq.cli.utils.MqttVersionConverter; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -29,49 +31,49 @@ import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; -public class ShellConnectTlsST { +class ShellConnectTlsST { @RegisterExtension - private final static HiveMQ hivemq = HiveMQ.builder().withTlsEnabled(true).build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().withTlsEnabled(true).build(); @RegisterExtension - final MqttCliShell mqttCliShell = new MqttCliShell(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_mutualTls(final char mqttVersion) throws Exception { - final String clientKeyPem = Resources.getResource("tls/client-key.pem").getPath(); final String clientCertPem = Resources.getResource("tls/client-cert.pem").getPath(); final String serverPem = Resources.getResource("tls/server.pem").getPath(); final List connectCommand = List.of( "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getMqttTlsPort()), - "-V", String.valueOf(mqttVersion), - "-i", "cliTest", + "-h", + HIVE_MQ.getHost(), + "-p", + String.valueOf(HIVE_MQ.getMqttTlsPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "cliTest", "--cafile", serverPem, "--key", clientKeyPem, "--cert", - clientCertPem - ); + clientCertPem); - mqttCliShell.executeAsync(connectCommand) - .awaitStdOut("Enter private key password:"); + mqttCliShell.executeAsync(connectCommand).awaitStdOut("Enter private key password:"); mqttCliShell.executeAsync(List.of("changeme")) - .awaitStdOut(String.format("cliTest@%s", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - assertConnectPacket(hivemq.getConnectPackets().get(0), connectAssertion -> { - connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); - }); - + assertConnectPacket( + HIVE_MQ.getConnectPackets().get(0), + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); } - } diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java index 5ccab3b4e..c1202f0c1 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectWebsocketsST.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.commands.shell.connect; +import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.cli.utils.broker.HiveMQ; import com.hivemq.cli.utils.cli.MqttCliShell; -import com.hivemq.cli.utils.MqttVersionConverter; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -29,13 +31,13 @@ import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; -public class ShellConnectWebsocketsST { +class ShellConnectWebsocketsST { @RegisterExtension - private final static HiveMQ hivemq = HiveMQ.builder().withWebsocketEnabled(true).build(); + private static final @NotNull HiveMQ HIVE_MQ = HiveMQ.builder().withWebsocketEnabled(true).build(); @RegisterExtension - final MqttCliShell mqttCliShell = new MqttCliShell(); + private final @NotNull MqttCliShell mqttCliShell = new MqttCliShell(); @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @@ -43,21 +45,26 @@ public class ShellConnectWebsocketsST { void test_defaultConnect(final char mqttVersion) throws Exception { final List connectCommand = List.of( "con", - "-h", hivemq.getHost(), - "-p", String.valueOf(hivemq.getWebsocketsPort()), - "-V", String.valueOf(mqttVersion), - "-i", "cliTest", + "-h", + HIVE_MQ.getHost(), + "-p", + String.valueOf(HIVE_MQ.getWebsocketsPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + "cliTest", "-ws", "-ws:path", - hivemq.getWebsocketsPath() - ); + HIVE_MQ.getWebsocketsPath()); mqttCliShell.executeAsync(connectCommand) - .awaitStdOut(String.format("cliTest@%s>", hivemq.getHost())) + .awaitStdOut(String.format("cliTest@%s>", HIVE_MQ.getHost())) .awaitLog("sending CONNECT") .awaitLog("received CONNACK"); - final ConnectPacket connectPacket = hivemq.getConnectPackets().get(0); - assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion))); + final ConnectPacket connectPacket = HIVE_MQ.getConnectPackets().get(0); + assertConnectPacket(connectPacket, + connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( + mqttVersion))); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java b/src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java index d080bd6e6..13ea36a54 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java +++ b/src/systemTest/java/com/hivemq/cli/utils/MqttVersionConverter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils; import com.hivemq.extension.sdk.api.packets.general.MqttVersion; @@ -26,7 +27,6 @@ public class MqttVersionConverter { } else if (version == '5') { return MqttVersion.V_5; } - throw new IllegalArgumentException("version " + version + " can not be converted to MqttVersion object."); } diff --git a/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java index 837fe4e35..045dec282 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java +++ b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils; import com.google.common.io.Resources; @@ -26,15 +27,14 @@ public class OrphanProcessCleanup { public static @NotNull Process startOrphanCleanupProcess(final @NotNull Process childProcess) throws IOException { - // We start the the OrphanCleanupProcess which sole job is to destroy the childProcess, meaning the mqtt-cli shell, + // We start the OrphanCleanupProcess which sole job is to destroy the childProcess, meaning the mqtt-cli shell, // when the jvm process exited final long jvmProcessId = ProcessHandle.current().pid(); final List orphanCleanupProcessCommand = List.of( System.getProperty("java"), Resources.getResource("OrphanCleanupProcess.java").getPath(), String.valueOf(jvmProcessId), - String.valueOf(childProcess.pid()) - ); + String.valueOf(childProcess.pid())); final Process orphanCleanupProcess = new ProcessBuilder(orphanCleanupProcessCommand).start(); // Wait until the process prints X, which means that the orphan cleanup process has sucessfully started diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java b/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java index 516f5b3f9..c9c27a1e6 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/HiveMQ.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.broker; import com.google.common.io.Resources; @@ -22,7 +23,6 @@ import com.hivemq.embedded.EmbeddedHiveMQ; import com.hivemq.embedded.EmbeddedHiveMQBuilder; import com.hivemq.extension.sdk.api.ExtensionMain; -import com.hivemq.extension.sdk.api.annotations.NotNull; import com.hivemq.extension.sdk.api.interceptor.connack.ConnackOutboundInterceptor; import com.hivemq.extension.sdk.api.interceptor.connect.ConnectInboundInterceptor; import com.hivemq.extension.sdk.api.packets.connack.ConnackPacket; @@ -37,6 +37,8 @@ import com.hivemq.extension.sdk.api.services.Services; import com.hivemq.migration.meta.PersistenceType; import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; @@ -49,26 +51,26 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertTrue; public class HiveMQ implements BeforeAllCallback, AfterAllCallback, AfterEachCallback { - private static final String WEBSOCKETS_PATH = "/mqtt-custom"; - private static final String BIND_ADDRESS = "localhost"; - - private EmbeddedHiveMQ hivemq; + private static final @NotNull String WEBSOCKETS_PATH = "/mqtt-custom"; + private static final @NotNull String BIND_ADDRESS = "localhost"; - private Path hivemqConfigFolder; - private Path hivemqDataFolder; + private @Nullable EmbeddedHiveMQ hivemq; + private @Nullable Path hivemqConfigFolder; + private @Nullable Path hivemqDataFolder; - private List connectPackets; - private List connackPackets; - private List publishPackets; - private List subscribePackets; - private List unsubscribePackets; - private List disconnectInformations; + private @Nullable List connectPackets; + private @Nullable List connackPackets; + private @Nullable List publishPackets; + private @Nullable List subscribePackets; + private @Nullable List unsubscribePackets; + private @Nullable List disconnectInformations; private int port = -1; private int tlsPort = -1; @@ -87,8 +89,7 @@ private HiveMQ(final boolean tlsEnabled, final boolean websocketEnabled) { } @Override - public void beforeAll(final ExtensionContext context) throws IOException { - + public void beforeAll(final @NotNull ExtensionContext context) throws IOException { this.port = generatePort(); this.connectPackets = new ArrayList<>(); this.connackPackets = new ArrayList<>(); @@ -99,7 +100,7 @@ public void beforeAll(final ExtensionContext context) throws IOException { final String tlsConfig = setupTls(); final String websocketsConfig = setupWebsockets(); - + //@formatter:off final String hivemqConfig = "\n" + " " + " \n" + " " + @@ -111,7 +112,7 @@ public void beforeAll(final ExtensionContext context) throws IOException { websocketsConfig + " \n" + ""; - + //@formatter:on this.hivemqConfigFolder = Files.createTempDirectory("hivemq-config-folder"); hivemqConfigFolder.toFile().deleteOnExit(); final File configXml = new File(hivemqConfigFolder.toAbsolutePath().toString(), "config.xml"); @@ -133,31 +134,32 @@ public void beforeAll(final ExtensionContext context) throws IOException { public void extensionStart( final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { - // Add Connect & Connack Interceptors - final ConnectInboundInterceptor connectInboundInterceptor = (connectInboundInput, connectInboundOutput) -> connectPackets.add(connectInboundInput.getConnectPacket()); - final ConnackOutboundInterceptor connackOutboundInterceptor = (connackOutboundInput, connackOutboundOutput) -> connackPackets.add(connackOutboundInput.getConnackPacket()); - Services.interceptorRegistry().setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); - Services.interceptorRegistry().setConnackOutboundInterceptorProvider(input -> connackOutboundInterceptor); + final ConnectInboundInterceptor connectInboundInterceptor = + (connectInboundInput, connectInboundOutput) -> connectPackets.add(connectInboundInput.getConnectPacket()); + final ConnackOutboundInterceptor connackOutboundInterceptor = + (connackOutboundInput, connackOutboundOutput) -> connackPackets.add(connackOutboundInput.getConnackPacket()); + Services.interceptorRegistry() + .setConnectInboundInterceptorProvider(input -> connectInboundInterceptor); + Services.interceptorRegistry() + .setConnackOutboundInterceptorProvider(input -> connackOutboundInterceptor); // Add all the other interceptors Services.initializerRegistry().setClientInitializer((initializerInput, clientContext) -> { - clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> { - disconnectInformations.add(new DisconnectInformation(disconnectInboundInput.getDisconnectPacket(), disconnectInboundInput.getClientInformation().getClientId())); - }); + clientContext.addDisconnectInboundInterceptor((disconnectInboundInput, disconnectInboundOutput) -> disconnectInformations.add( + new DisconnectInformation( + disconnectInboundInput.getDisconnectPacket(), + disconnectInboundInput.getClientInformation().getClientId()))); - clientContext.addPublishInboundInterceptor((publishInboundInput, publishInboundOutput) -> { - publishPackets.add(publishInboundInput.getPublishPacket()); - }); + clientContext.addPublishInboundInterceptor((publishInboundInput, publishInboundOutput) -> publishPackets.add( + publishInboundInput.getPublishPacket())); - clientContext.addSubscribeInboundInterceptor((subscribeInboundInput, subscribeInboundOutput) -> { - subscribePackets.add(subscribeInboundInput.getSubscribePacket()); - }); + clientContext.addSubscribeInboundInterceptor((subscribeInboundInput, subscribeInboundOutput) -> subscribePackets.add( + subscribeInboundInput.getSubscribePacket())); - clientContext.addUnsubscribeInboundInterceptor((unsubscribeInboundInput, unsubscribeInboundOutput) -> { - unsubscribePackets.add(unsubscribeInboundInput.getUnsubscribePacket()); - }); + clientContext.addUnsubscribeInboundInterceptor((unsubscribeInboundInput, unsubscribeInboundOutput) -> unsubscribePackets.add( + unsubscribeInboundInput.getUnsubscribePacket())); }); } @@ -167,7 +169,8 @@ public void extensionStop( final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { } - }).build(); + }) + .build(); final EmbeddedHiveMQBuilder builder = EmbeddedHiveMQ.builder() .withConfigurationFolder(hivemqConfigFolder) @@ -185,44 +188,44 @@ public void extensionStop( } @Override - public void afterAll(final ExtensionContext context) throws IOException { - hivemq.stop(); - FileUtils.deleteDirectory(hivemqConfigFolder.toFile()); - FileUtils.deleteDirectory(hivemqDataFolder.toFile()); + public void afterAll(final @NotNull ExtensionContext context) throws IOException { + Objects.requireNonNull(hivemq).stop(); + FileUtils.deleteDirectory(Objects.requireNonNull(hivemqConfigFolder).toFile()); + FileUtils.deleteDirectory(Objects.requireNonNull(hivemqDataFolder).toFile()); } @Override - public void afterEach(final ExtensionContext context) { - connectPackets.clear(); - connackPackets.clear(); - disconnectInformations.clear(); - publishPackets.clear(); - subscribePackets.clear(); - unsubscribePackets.clear(); + public void afterEach(final @NotNull ExtensionContext context) { + Objects.requireNonNull(connectPackets).clear(); + Objects.requireNonNull(connackPackets).clear(); + Objects.requireNonNull(disconnectInformations).clear(); + Objects.requireNonNull(publishPackets).clear(); + Objects.requireNonNull(subscribePackets).clear(); + Objects.requireNonNull(unsubscribePackets).clear(); } public @NotNull List getConnectPackets() { - return connectPackets; + return Objects.requireNonNull(connectPackets); } public @NotNull List getConnackPackets() { - return connackPackets; + return Objects.requireNonNull(connackPackets); } public @NotNull List getPublishPackets() { - return publishPackets; + return Objects.requireNonNull(publishPackets); } public @NotNull List getSubscribePackets() { - return subscribePackets; + return Objects.requireNonNull(subscribePackets); } public @NotNull List getUnsubscribePackets() { - return unsubscribePackets; + return Objects.requireNonNull(unsubscribePackets); } public @NotNull List getDisconnectInformations() { - return disconnectInformations; + return Objects.requireNonNull(disconnectInformations); } public int getMqttPort() { @@ -263,6 +266,7 @@ private int generatePort() throws IOException { this.tlsPort = generatePort(); final String brokerKeyStorePath = Resources.getResource("tls/broker-keystore.jks").getPath(); final String brokerTrustStorePath = Resources.getResource("tls/client-keystore.jks").getPath(); + //@formatter:off tlsConfig = "\n" + " " + tlsPort + "\n" + @@ -280,6 +284,7 @@ private int generatePort() throws IOException { " \n" + " \n" + "\n"; + //@formatter:on } return tlsConfig; } @@ -288,23 +293,18 @@ private int generatePort() throws IOException { String websocketsConfig = ""; if (websocketEnabled) { this.websocketsPort = generatePort(); - websocketsConfig = - "\n" + - " " + websocketsPort + "\n" + - " " + BIND_ADDRESS + "\n" + - " " + WEBSOCKETS_PATH + "\n" + - " my-websocket-listener\n" + - " \n" + - " mqttv3.1\n" + - " mqtt\n" + - " \n" + - " true\n" + - ""; + websocketsConfig = "\n" + " " + websocketsPort + "\n" + + " " + BIND_ADDRESS + "\n" + " " + + WEBSOCKETS_PATH + "\n" + " my-websocket-listener\n" + + " \n" + " mqttv3.1\n" + + " mqtt\n" + " \n" + + " true\n" + ""; } return websocketsConfig; } public static class Builder { + private boolean tlsEnabled = false; private boolean websocketEnabled = false; diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/ConnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/ConnectAssertion.java index 7acb962a3..923c9d55f 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/ConnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/ConnectAssertion.java @@ -17,7 +17,6 @@ package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; -import com.hivemq.extension.sdk.api.annotations.Nullable; import com.hivemq.extension.sdk.api.packets.connect.ConnectPacket; import com.hivemq.extension.sdk.api.packets.connect.WillPublishPacket; import com.hivemq.extension.sdk.api.packets.general.MqttVersion; @@ -25,6 +24,7 @@ import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.nio.ByteBuffer; import java.util.Optional; @@ -45,10 +45,8 @@ public class ConnectAssertion { private boolean requestProblemInformation = true; private boolean requestResponseInformation = false; - private @NotNull Optional userName = Optional.empty(); - private @NotNull Optional password = Optional.empty(); - private @NotNull Optional authenticationMethod = Optional.empty(); - private @NotNull Optional authenticationData = Optional.empty(); + private @Nullable String userName = null; + private @Nullable ByteBuffer password = null; private @Nullable WillPublishPacket willPublish = null; private @Nullable UserProperties userProperties = @@ -73,10 +71,8 @@ public static void assertConnectPacket( assertEquals(connectAssertion.requestProblemInformation, connectPacket.getRequestProblemInformation()); assertEquals(connectAssertion.requestResponseInformation, connectPacket.getRequestResponseInformation()); - assertEquals(connectAssertion.userName, connectPacket.getUserName()); - assertEquals(connectAssertion.password, connectPacket.getPassword()); - assertEquals(connectAssertion.authenticationMethod, connectPacket.getAuthenticationMethod()); - assertEquals(connectAssertion.authenticationData, connectPacket.getAuthenticationData()); + assertEquals(Optional.ofNullable(connectAssertion.userName), connectPacket.getUserName()); + assertEquals(Optional.ofNullable(connectAssertion.password), connectPacket.getPassword()); assertEquals(connectAssertion.userProperties, connectPacket.getUserProperties()); if (connectAssertion.willPublish != null) { @@ -140,19 +136,11 @@ public void setRequestResponseInformation(final boolean requestResponseInformati } public void setUserName(final @Nullable String userName) { - this.userName = Optional.of(userName); + this.userName = userName; } public void setPassword(final @Nullable ByteBuffer password) { - this.password = Optional.of(password); - } - - public void setAuthenticationMethod(final @Nullable String authenticationMethod) { - this.authenticationMethod = Optional.of(authenticationMethod); - } - - public void setAuthenticationData(final @Nullable ByteBuffer authenticationData) { - this.authenticationData = Optional.of(authenticationData); + this.password = password; } public void setWillPublish(final @Nullable WillPublishPacket willPublish) { @@ -162,5 +150,4 @@ public void setWillPublish(final @Nullable WillPublishPacket willPublish) { public void setUserProperties(final @Nullable UserProperties userProperties) { this.userProperties = userProperties; } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectAssertion.java index 9ee0257c8..6da9ae4c8 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectAssertion.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; -import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectReasonCode; import com.hivemq.extension.sdk.api.packets.general.UserProperties; import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; @@ -31,43 +31,36 @@ public class DisconnectAssertion { - private @NotNull DisconnectReasonCode disconnectReasonCode = DisconnectReasonCode.NORMAL_DISCONNECTION; - private @NotNull Optional reasonString = Optional.empty(); - private @NotNull Optional serverReference = Optional.empty(); - private @NotNull UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.builder().build()); - private @NotNull Optional sessionExpiryInterval = Optional.empty(); + private @Nullable String reasonString = null; + private @NotNull UserProperties userProperties = + UserPropertiesImpl.of(ImmutableList.builder().build()); + private @Nullable Long sessionExpiryInterval = null; private @Nullable String disconnectedClient = null; private DisconnectAssertion() { } - public static void assertDisconnectPacket(final @NotNull DisconnectInformation disconnectInformation, final @NotNull Consumer disconnectAssertionConsumer) { + public static void assertDisconnectPacket( + final @NotNull DisconnectInformation disconnectInformation, + final @NotNull Consumer disconnectAssertionConsumer) { final DisconnectAssertion disconnectAssertion = new DisconnectAssertion(); disconnectAssertionConsumer.accept(disconnectAssertion); final DisconnectPacket disconnectPacket = disconnectInformation.getDisconnectPacket(); - assertEquals(disconnectAssertion.disconnectReasonCode, disconnectPacket.getReasonCode()); - assertEquals(disconnectAssertion.reasonString, disconnectPacket.getReasonString()); - assertEquals(disconnectAssertion.serverReference, disconnectPacket.getServerReference()); + assertEquals(Optional.ofNullable(disconnectAssertion.reasonString), disconnectPacket.getReasonString()); assertEquals(disconnectAssertion.userProperties, disconnectPacket.getUserProperties()); - assertEquals(disconnectAssertion.sessionExpiryInterval, disconnectPacket.getSessionExpiryInterval()); + assertEquals( + Optional.ofNullable(disconnectAssertion.sessionExpiryInterval), + disconnectPacket.getSessionExpiryInterval()); if (disconnectAssertion.disconnectedClient != null) { assertEquals(disconnectAssertion.disconnectedClient, disconnectInformation.getClientId()); } } - public void setDisconnectReasonCode(final @NotNull DisconnectReasonCode disconnectReasonCode) { - this.disconnectReasonCode = disconnectReasonCode; - } - public void setReasonString(final @NotNull String reasonString) { - this.reasonString = Optional.of(reasonString); - } - - public void setServerReference(final @NotNull String serverReference) { - this.serverReference = Optional.of(serverReference); + this.reasonString = reasonString; } public void setUserProperties(final @NotNull UserProperties userProperties) { @@ -75,7 +68,7 @@ public void setUserProperties(final @NotNull UserProperties userProperties) { } public void setSessionExpiryInterval(final long sessionExpiryInterval) { - this.sessionExpiryInterval = Optional.of(sessionExpiryInterval); + this.sessionExpiryInterval = sessionExpiryInterval; } public void setDisconnectedClient(final @NotNull String disconnectedClient) { diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectInformation.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectInformation.java index acc1f729e..e14a727a4 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectInformation.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/DisconnectInformation.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.broker.assertions; import com.hivemq.extension.sdk.api.packets.disconnect.DisconnectPacket; import org.jetbrains.annotations.NotNull; public class DisconnectInformation { + private final @NotNull DisconnectPacket disconnectPacket; private final @NotNull String clientId; diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/PublishAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/PublishAssertion.java index 8489b7c7e..07ccff282 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/PublishAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/PublishAssertion.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; @@ -23,6 +24,7 @@ import com.hivemq.extensions.packets.general.UserPropertiesImpl; import com.hivemq.mqtt.message.mqtt5.MqttUserProperty; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.nio.ByteBuffer; import java.util.Optional; @@ -35,30 +37,33 @@ public class PublishAssertion { private @NotNull Qos qos = Qos.AT_MOST_ONCE; private boolean retain = false; private @NotNull String topic = ""; - private @NotNull Optional payload = Optional.empty(); - private @NotNull Optional correlationData = Optional.empty(); - private @NotNull Optional contentType = Optional.empty(); - private @NotNull Optional responseTopic = Optional.empty(); - private @NotNull Optional messageExpiryInterval = Optional.of(4294967296L); - private @NotNull Optional payloadFormatIndicator = Optional.empty(); - private @NotNull UserProperties userProperties = UserPropertiesImpl.of(ImmutableList.builder().build()); + private @Nullable ByteBuffer payload = null; + private @Nullable ByteBuffer correlationData = null; + private @Nullable String contentType = null; + private @Nullable String responseTopic = null; + private @Nullable Long messageExpiryInterval = 4294967296L; + private @Nullable PayloadFormatIndicator payloadFormatIndicator = null; + private @NotNull UserProperties userProperties = + UserPropertiesImpl.of(ImmutableList.builder().build()); private PublishAssertion() { } - public static void assertPublishPacket(final @NotNull PublishPacket publishPacket, final @NotNull Consumer publishAssertionConsumer) { + public static void assertPublishPacket( + final @NotNull PublishPacket publishPacket, + final @NotNull Consumer publishAssertionConsumer) { final PublishAssertion publishAssertion = new PublishAssertion(); publishAssertionConsumer.accept(publishAssertion); assertEquals(publishAssertion.qos, publishPacket.getQos()); assertEquals(publishAssertion.retain, publishPacket.getRetain()); assertEquals(publishAssertion.topic, publishPacket.getTopic()); - assertEquals(publishAssertion.payload, publishPacket.getPayload()); - assertEquals(publishAssertion.correlationData, publishPacket.getCorrelationData()); - assertEquals(publishAssertion.contentType, publishPacket.getContentType()); - assertEquals(publishAssertion.responseTopic, publishPacket.getResponseTopic()); - assertEquals(publishAssertion.messageExpiryInterval, publishPacket.getMessageExpiryInterval()); - assertEquals(publishAssertion.payloadFormatIndicator, publishPacket.getPayloadFormatIndicator()); + assertEquals(Optional.ofNullable(publishAssertion.payload), publishPacket.getPayload()); + assertEquals(Optional.ofNullable(publishAssertion.correlationData), publishPacket.getCorrelationData()); + assertEquals(Optional.ofNullable(publishAssertion.contentType), publishPacket.getContentType()); + assertEquals(Optional.ofNullable(publishAssertion.responseTopic), publishPacket.getResponseTopic()); + assertEquals(Optional.ofNullable(publishAssertion.messageExpiryInterval), publishPacket.getMessageExpiryInterval()); + assertEquals(Optional.ofNullable(publishAssertion.payloadFormatIndicator), publishPacket.getPayloadFormatIndicator()); assertEquals(publishAssertion.userProperties, publishPacket.getUserProperties()); } @@ -75,27 +80,27 @@ public void setTopic(final @NotNull String topic) { } public void setPayload(final @NotNull ByteBuffer payload) { - this.payload = Optional.of(payload); + this.payload = payload; } public void setCorrelationData(final @NotNull ByteBuffer correlationData) { - this.correlationData = Optional.of(correlationData); + this.correlationData = correlationData; } - public void setContentType(final String contentType) { - this.contentType = Optional.of(contentType); + public void setContentType(final @NotNull String contentType) { + this.contentType = contentType; } public void setResponseTopic(final @NotNull String responseTopic) { - this.responseTopic = Optional.of(responseTopic); + this.responseTopic = responseTopic; } public void setMessageExpiryInterval(final long messageExpiryInterval) { - this.messageExpiryInterval = Optional.of(messageExpiryInterval); + this.messageExpiryInterval = messageExpiryInterval; } public void setPayloadFormatIndicator(final @NotNull PayloadFormatIndicator payloadFormatIndicator) { - this.payloadFormatIndicator = Optional.of(payloadFormatIndicator); + this.payloadFormatIndicator = payloadFormatIndicator; } public void setUserProperties(final @NotNull UserProperties userProperties) { diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/SubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/SubscribeAssertion.java index 90fc2979a..5d50b04c9 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/SubscribeAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/SubscribeAssertion.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; @@ -35,7 +36,9 @@ public class SubscribeAssertion { private SubscribeAssertion() { } - public static void assertSubscribePacket(final @NotNull SubscribePacket subscribePacket, final @NotNull Consumer subscribeAssertionConsumer) { + public static void assertSubscribePacket( + final @NotNull SubscribePacket subscribePacket, + final @NotNull Consumer subscribeAssertionConsumer) { final SubscribeAssertion subscribeAssertion = new SubscribeAssertion(); subscribeAssertionConsumer.accept(subscribeAssertion); assertEquals(subscribeAssertion.subscriptions, subscribePacket.getSubscriptions()); diff --git a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/UnsubscribeAssertion.java b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/UnsubscribeAssertion.java index 161d16abc..6137d3d8f 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/UnsubscribeAssertion.java +++ b/src/systemTest/java/com/hivemq/cli/utils/broker/assertions/UnsubscribeAssertion.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.broker.assertions; import com.google.common.collect.ImmutableList; @@ -37,7 +38,6 @@ private UnsubscribeAssertion() { public static void assertUnsubscribePacket( final @NotNull UnsubscribePacket unsubscribePacket, final @NotNull Consumer unsubscribeAssertionConsumer) { - final UnsubscribeAssertion unsubscribeAssertion = new UnsubscribeAssertion(); unsubscribeAssertionConsumer.accept(unsubscribeAssertion); diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCli.java b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCli.java index 3f556ce7d..98a69a8b5 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCli.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCli.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.cli; import com.hivemq.cli.utils.cli.results.ExecutionResult; @@ -34,15 +35,19 @@ public class MqttCli { .collect(Collectors.toUnmodifiableList()); /** - * Executes a mqtt-cli command in blocking manner. This method should be used for all mqtt-cli commands which - * exit the cli with an exit code. - * @param command the command to execute with the mqtt cli + * Executes a mqtt-cli command in blocking manner. This method should be used for all mqtt-cli commands which exit + * the cli with an exit code. + * + * @param command the command to execute with the mqtt cli * @param environmentVariables the environment variables to start the process with - * @return an {@link ExecutionResult} which contains the std-output, err-ouput and exit-code of the command's execution - * @throws IOException when an error occurred while starting the process or reading its output + * @return an {@link ExecutionResult} which contains the std-output, err-ouput and exit-code of the command's + * execution + * @throws IOException when an error occurred while starting the process or reading its output * @throws InterruptedException when the process was interrupted */ - public static @NotNull ExecutionResult execute(final @NotNull List command, final @NotNull Map environmentVariables) throws IOException, InterruptedException { + public static @NotNull ExecutionResult execute( + final @NotNull List command, final @NotNull Map environmentVariables) + throws IOException, InterruptedException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); @@ -65,15 +70,17 @@ public class MqttCli { } /** - * Executes a mqtt-cli command in blocking manner. This method should be used for all mqtt-cli commands which - * exit the cli with an exit code. + * Executes a mqtt-cli command in blocking manner. This method should be used for all mqtt-cli commands which exit + * the cli with an exit code. + * * @param command the command to execute with the mqtt cli - * @return an {@link ExecutionResult} which contains the std-output, err-ouput and exit-code of the command's execution - * @throws IOException when an error occurred while starting the process or reading its output + * @return an {@link ExecutionResult} which contains the std-output, err-ouput and exit-code of the command's + * execution + * @throws IOException when an error occurred while starting the process or reading its output * @throws InterruptedException when the process was interrupted */ - public static @NotNull ExecutionResult execute(final @NotNull List command) throws IOException, InterruptedException { + public static @NotNull ExecutionResult execute(final @NotNull List command) + throws IOException, InterruptedException { return execute(command, Map.of()); } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliAsync.java b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliAsync.java index ea376fe70..f174085b3 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliAsync.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliAsync.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.cli; import com.hivemq.cli.utils.OrphanProcessCleanup; @@ -32,7 +33,7 @@ public class MqttCliAsync implements AfterEachCallback { - final List startedProcesses = new ArrayList<>(); + private final @NotNull List startedProcesses = new ArrayList<>(); @Override public void afterEach(final @NotNull ExtensionContext context) { @@ -44,12 +45,15 @@ public void afterEach(final @NotNull ExtensionContext context) { /** * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not * exit the process like the subscribe command. - * @param command the command to execute with the mqtt cli + * + * @param command the command to execute with the mqtt cli * @param environmentVariables the environment variables to start the process with - * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages + * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write + * messages * @throws IOException when an error occurred while starting the process */ - public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command, final @NotNull Map environmentVariables) + public @NotNull ExecutionResultAsync executeAsync( + final @NotNull List command, final @NotNull Map environmentVariables) throws IOException { final List fullCommand = new ArrayList<>(CLI_EXEC); assertTrue(fullCommand.addAll(command)); @@ -70,13 +74,13 @@ public void afterEach(final @NotNull ExtensionContext context) { /** * Executes a mqtt-cli command asynchronously. This method should be used for all mqtt-cli commands which do not * exit the process like the subscribe command. + * * @param command the command to execute with the mqtt cli - * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write messages + * @return an {@link ExecutionResultAsync} which can be used to wait for std-out std-err messages and write + * messages * @throws IOException when an error occurred while starting the process */ - public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command) - throws IOException { + public @NotNull ExecutionResultAsync executeAsync(final @NotNull List command) throws IOException { return executeAsync(command, Map.of()); } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java index aea890e77..48f6c8e35 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/MqttCliShell.java @@ -24,6 +24,7 @@ import com.hivemq.cli.utils.cli.results.AwaitOutput; import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -35,21 +36,22 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; -import static com.hivemq.cli.utils.cli.MqttCli.CLI_EXEC; import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; +import static com.hivemq.cli.utils.cli.MqttCli.CLI_EXEC; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; public class MqttCliShell implements BeforeEachCallback, AfterEachCallback { - public static String DEFAULT_CLIENT_NAME = "cliTest"; + public static @NotNull String DEFAULT_CLIENT_NAME = "cliTest"; - private Path homeDir; - private ProcessIO processIO; - private Process process; - private LogWaiter logWaiter; - private Process orphanCleanupProcess; + private @Nullable Path homeDir; + private @Nullable ProcessIO processIO; + private @Nullable Process process; + private @Nullable LogWaiter logWaiter; + private @Nullable Process orphanCleanupProcess; private int connectClientMarker = 0; private final @NotNull Map envVariables; @@ -79,9 +81,15 @@ public void beforeEach(final @NotNull ExtensionContext context) throws Exception @Override public void afterEach(final @NotNull ExtensionContext context) throws IOException { - FileUtils.deleteDirectory(homeDir.toFile()); - process.destroyForcibly(); - orphanCleanupProcess.destroyForcibly(); + if (homeDir != null && homeDir.toFile().exists()) { + FileUtils.deleteDirectory(homeDir.toFile()); + } + if (process != null) { + process.destroyForcibly(); + } + if (orphanCleanupProcess != null) { + orphanCleanupProcess.destroyForcibly(); + } } private @NotNull Process startShellMode(final @NotNull Path homeDir) throws IOException { @@ -117,7 +125,8 @@ public void afterEach(final @NotNull ExtensionContext context) throws IOExceptio } /** - * Connects a mqtt-client with client-id {@link #DEFAULT_CLIENT_NAME} and awaits the successful output statements on std-out and in the logfile. + * Connects a mqtt-client with client-id {@link #DEFAULT_CLIENT_NAME} and awaits the successful output statements on + * std-out and in the logfile. * * @param hivemq the HiveMQ instance to which the client should connect * @throws IOException when the cli command to connect could not be written to the shell @@ -127,15 +136,24 @@ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion) } /** - * Connects a mqtt-client with the given client-id and awaits the successful output statements on std-out and in the logfile. + * Connects a mqtt-client with the given client-id and awaits the successful output statements on std-out and in the + * logfile. * * @param hivemq the HiveMQ instance to which the client should connect * @throws IOException when the cli command to connect could not be written to the shell */ - public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, final @NotNull String clientId) throws IOException { - final List connectCommand = - List.of("con", "-h", hivemq.getHost(), "-p", String.valueOf(hivemq.getMqttPort()), "-V", String.valueOf(mqttVersion), "-i", clientId); - + public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, final @NotNull String clientId) + throws IOException { + final List connectCommand = List.of( + "con", + "-h", + hivemq.getHost(), + "-p", + String.valueOf(hivemq.getMqttPort()), + "-V", + String.valueOf(mqttVersion), + "-i", + clientId); final AwaitOutput awaitOutput = executeAsync(connectCommand).awaitStdOut(String.format("%s@%s>", clientId, hivemq.getHost())); @@ -146,7 +164,7 @@ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, connectAssertion.setClientId(clientId); }); - connectClientMarker+= 1; + connectClientMarker += 1; } /** @@ -159,16 +177,17 @@ public void connectClient(final @NotNull HiveMQ hivemq, final char mqttVersion, */ public @NotNull AwaitOutput executeAsync(final @NotNull List command) throws IOException { final String fullCommand = String.join(" ", command); - processIO.writeMsg(fullCommand); + Objects.requireNonNull(processIO).writeMsg(fullCommand); return new AwaitOutput(processIO, logWaiter, fullCommand); } /** * Check if the mqtt-cli shell process is alive. + * * @return true if the mqtt-cli shell process is alive. */ public boolean isAlive() { - return process.isAlive(); + return Objects.requireNonNull(process).isAlive(); } } diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/io/LogWaiter.java b/src/systemTest/java/com/hivemq/cli/utils/cli/io/LogWaiter.java index cce35b347..79356524e 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/io/LogWaiter.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/io/LogWaiter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.cli.io; import com.hivemq.cli.utils.exceptions.TimeoutException; @@ -26,6 +27,7 @@ import java.util.concurrent.atomic.AtomicReference; import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; public class LogWaiter { @@ -38,46 +40,41 @@ public LogWaiter(final @NotNull File logFile) { } public void awaitLog(final @NotNull String expectedLogMessage) throws TimeoutException { - final AtomicReference readLog = new AtomicReference<>(); try { await().until(() -> { final StringBuilder readChars = new StringBuilder(); try { - final FileReader fileReader = new FileReader(logFile, StandardCharsets.UTF_8); - fileReader.skip(filePosition.get()); - - while (true) { - for (int i = 0; i < expectedLogMessage.length(); i++) { - final int readChar = fileReader.read(); - readChars.append((char) readChar); - - if (readChar == -1) { - return false; - } - - if (expectedLogMessage.charAt(i) != readChar) { - break; - } - - - if (i == expectedLogMessage.length() - 1) { - filePosition.set(filePosition.get() + readChars.length()); - return true; + try (final FileReader fileReader = new FileReader(logFile, StandardCharsets.UTF_8)) { + assertEquals(filePosition.get(), fileReader.skip(filePosition.get())); + while (true) { + for (int i = 0; i < expectedLogMessage.length(); i++) { + final int readChar = fileReader.read(); + readChars.append((char) readChar); + + if (readChar == -1) { + return false; + } + + if (expectedLogMessage.charAt(i) != readChar) { + break; + } + + + if (i == expectedLogMessage.length() - 1) { + filePosition.set(filePosition.get() + readChars.length()); + return true; + } } } } } finally { readLog.set(readChars.toString()); } - }); } catch (final ConditionTimeoutException e) { throw new TimeoutException(e, readLog.get()); } - } - } - diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/io/ProcessIO.java b/src/systemTest/java/com/hivemq/cli/utils/cli/io/ProcessIO.java index 1de97d32a..fef0881e8 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/io/ProcessIO.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/io/ProcessIO.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.cli.io; import com.hivemq.cli.utils.exceptions.TimeoutException; @@ -52,7 +53,6 @@ private ProcessIO( } public static @NotNull ProcessIO startReading(final @NotNull Process process) { - final StringBuilder processStdOut = new StringBuilder(); final StringBuilder processErrOut = new StringBuilder(); final BufferedWriter processOutputWriter = @@ -151,5 +151,4 @@ public void writeMsg(final @NotNull String message) throws IOException { processOutputWriter.write('\n'); processOutputWriter.flush(); } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/results/AwaitOutput.java b/src/systemTest/java/com/hivemq/cli/utils/cli/results/AwaitOutput.java index 6743dbfdb..b3123527c 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/results/AwaitOutput.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/results/AwaitOutput.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.cli.results; import com.hivemq.cli.utils.cli.io.LogWaiter; @@ -30,7 +31,8 @@ public class AwaitOutput { private final @NotNull String command; private final @Nullable LogWaiter logWaiter; - public AwaitOutput(final @NotNull ProcessIO processIO, final @Nullable LogWaiter logWaiter, final @NotNull String command) { + public AwaitOutput( + final @NotNull ProcessIO processIO, final @Nullable LogWaiter logWaiter, final @NotNull String command) { this.processIO = processIO; this.command = command; this.logWaiter = logWaiter; @@ -40,7 +42,11 @@ public AwaitOutput(final @NotNull ProcessIO processIO, final @Nullable LogWaiter try { processIO.awaitStdOut(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Command '%s' did not return expected standard output '%s' in time. Actual read standard output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + Assertions.fail(String.format( + "Command '%s' did not return expected standard output '%s' in time. Actual read standard output: '%s'", + command, + expectedOutput, + timeoutException.getActualOutput()), timeoutException); } return this; } @@ -49,7 +55,11 @@ public AwaitOutput(final @NotNull ProcessIO processIO, final @Nullable LogWaiter try { processIO.awaitStdErr(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Command '%s' did not return expected error output '%s' in time. Actual read error output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + Assertions.fail(String.format( + "Command '%s' did not return expected error output '%s' in time. Actual read error output: '%s'", + command, + expectedOutput, + timeoutException.getActualOutput()), timeoutException); } return this; } @@ -59,9 +69,12 @@ public AwaitOutput(final @NotNull ProcessIO processIO, final @Nullable LogWaiter try { logWaiter.awaitLog(expectedLogMessage); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Command '%s' did not return expected logfile output '%s' in time. Actual read logfile output: '%s'", command, expectedLogMessage, timeoutException.getActualOutput()), timeoutException); + Assertions.fail(String.format( + "Command '%s' did not return expected logfile output '%s' in time. Actual read logfile output: '%s'", + command, + expectedLogMessage, + timeoutException.getActualOutput()), timeoutException); } return this; } - } diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResult.java b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResult.java index b33d80a1f..fb8288784 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResult.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResult.java @@ -13,16 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.cli.results; import org.jetbrains.annotations.NotNull; public class ExecutionResult { + private final int exitCode; private final @NotNull String standardOutput; private final @NotNull String errorOutput; - public ExecutionResult(final int exitCode, final @NotNull String standardOutput, final @NotNull String errorOutput) { + public ExecutionResult( + final int exitCode, final @NotNull String standardOutput, final @NotNull String errorOutput) { this.exitCode = exitCode; this.standardOutput = standardOutput; this.errorOutput = errorOutput; diff --git a/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResultAsync.java b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResultAsync.java index 69c71e35c..a1f55f234 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResultAsync.java +++ b/src/systemTest/java/com/hivemq/cli/utils/cli/results/ExecutionResultAsync.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.cli.results; import com.hivemq.cli.utils.cli.io.ProcessIO; @@ -36,7 +37,11 @@ public ExecutionResultAsync(final @NotNull ProcessIO processIO, final @NotNull S try { processIO.awaitStdOut(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Command '%s' did not return expected standard output '%s' in time. Actual read standard output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + Assertions.fail(String.format( + "Command '%s' did not return expected standard output '%s' in time. Actual read standard output: '%s'", + command, + expectedOutput, + timeoutException.getActualOutput()), timeoutException); } return this; } @@ -45,7 +50,11 @@ public ExecutionResultAsync(final @NotNull ProcessIO processIO, final @NotNull S try { processIO.awaitStdErr(expectedOutput); } catch (final TimeoutException timeoutException) { - Assertions.fail(String.format("Command '%s' did not return expected error output '%s' in time. Actual read error output: '%s'", command, expectedOutput, timeoutException.getActualOutput()), timeoutException); + Assertions.fail(String.format( + "Command '%s' did not return expected error output '%s' in time. Actual read error output: '%s'", + command, + expectedOutput, + timeoutException.getActualOutput()), timeoutException); } return this; } diff --git a/src/systemTest/java/com/hivemq/cli/utils/exceptions/TimeoutException.java b/src/systemTest/java/com/hivemq/cli/utils/exceptions/TimeoutException.java index bc82930fd..d16ff4077 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/exceptions/TimeoutException.java +++ b/src/systemTest/java/com/hivemq/cli/utils/exceptions/TimeoutException.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.hivemq.cli.utils.exceptions; import org.jetbrains.annotations.NotNull; From 8e851dbf725bd29ac1d2033de49a333854ec20aa Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Thu, 15 Dec 2022 13:47:50 +0100 Subject: [PATCH 61/64] NegatableOptions > Moved all fixes from Master to Mixins --- .../cli/commands/options/ConnectOptions.java | 8 +- .../options/ConnectRestrictionOptions.java | 81 +++++++++++------ .../cli/commands/options/PublishOptions.java | 74 +++++++++++----- .../cli/commands/options/WillOptions.java | 88 +++++++++++++------ 4 files changed, 171 insertions(+), 80 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java index bcbd96ef0..c606311a0 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java @@ -66,8 +66,8 @@ public class ConnectOptions { private @Nullable Integer keepAlive; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-c", "--cleanStart"}, negatable = true, defaultValue = "true", - description = "Define a clean start for the connection (default: true)") + @CommandLine.Option(names = {"--no-cleanStart"}, negatable = true, defaultValue = "true", + description = "Define a clean start for the connection (default: true)") private boolean cleanStart; @SuppressWarnings("unused") @@ -81,10 +81,10 @@ public class ConnectOptions { private @Nullable Mqtt5UserProperty @Nullable [] connectUserProperties; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-ws"}, description = "Use WebSocket transport protocol (default: false)", order = 2) + @CommandLine.Option(names = {"-ws"}, description = "Use WebSocket transport protocol (default: false)") private boolean useWebSocket; - @CommandLine.Option(names = {"-ws:path"}, description = "The path of the WebSocket", order = 2) + @CommandLine.Option(names = {"-ws:path"}, description = "The path of the WebSocket") private @Nullable String webSocketPath; @CommandLine.Mixin diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java index d7a226c09..13f9abf4d 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectRestrictionOptions.java @@ -26,52 +26,66 @@ public class ConnectRestrictionOptions { @SuppressWarnings("unused") - @CommandLine.Option(names = {"--rcvMax"}, description = - "The maximum amount of not acknowledged publishes with QoS 1 or 2 the client accepts from the server concurrently. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_RECEIVE_MAXIMUM + ")") + @CommandLine.Option(names = {"--rcvMax"}, + description = + "The maximum amount of not acknowledged publishes with QoS 1 or 2 the client accepts from the server concurrently. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_RECEIVE_MAXIMUM + + ")") private @Nullable Integer receiveMaximum; @SuppressWarnings("unused") - @CommandLine.Option(names = {"--sendMax"}, description = - "The maximum amount of not acknowledged publishes with QoS 1 or 2 the client send to the server concurrently. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM + ")") + @CommandLine.Option(names = {"--sendMax"}, + description = + "The maximum amount of not acknowledged publishes with QoS 1 or 2 the client send to the server concurrently. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM + + ")") private @Nullable Integer sendMaximum; @SuppressWarnings("unused") @CommandLine.Option(names = {"--maxPacketSize"}, - description = "The maximum packet size the client accepts from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_MAXIMUM_PACKET_SIZE + ")") + description = "The maximum packet size the client accepts from the server. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_MAXIMUM_PACKET_SIZE + + ")") private @Nullable Integer maximumPacketSize; @SuppressWarnings("unused") @CommandLine.Option(names = {"--sendMaxPacketSize"}, - description = "The maximum packet size the client sends to the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM_PACKET_SIZE + ")") + description = "The maximum packet size the client sends to the server. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_SEND_MAXIMUM_PACKET_SIZE + + ")") private @Nullable Integer sendMaximumPacketSize; @SuppressWarnings("unused") @CommandLine.Option(names = {"--topicAliasMax"}, - description = "The maximum amount of topic aliases the client accepts from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_TOPIC_ALIAS_MAXIMUM + ")") + description = + "The maximum amount of topic aliases the client accepts from the server. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_TOPIC_ALIAS_MAXIMUM + + ")") private @Nullable Integer topicAliasMaximum; @SuppressWarnings("unused") @CommandLine.Option(names = {"--sendTopicAliasMax"}, - description = "The maximum amount of topic aliases the client sends to the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_SEND_TOPIC_ALIAS_MAXIMUM + ")") + description = "The maximum amount of topic aliases the client sends to the server. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_SEND_TOPIC_ALIAS_MAXIMUM + + ")") private @Nullable Integer sendTopicAliasMaximum; @SuppressWarnings({"unused", "FieldMayBeFinal"}) - @CommandLine.Option(names = {"--reqProblemInfo"}, negatable = true, - description = "The client requests problem information from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_REQUEST_PROBLEM_INFORMATION + ")") - private boolean requestProblemInformation = Mqtt5ConnectRestrictions.DEFAULT_REQUEST_PROBLEM_INFORMATION; + @CommandLine.Option(names = {"--no-reqProblemInfo"}, + negatable = true, + description = "The client requests problem information from the server. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_REQUEST_PROBLEM_INFORMATION + + ")", + defaultValue = "true") + private boolean requestProblemInformation; @SuppressWarnings({"unused", "FieldMayBeFinal"}) - @CommandLine.Option(names = {"--reqResponseInfo"}, negatable = true, - description = "The client requests response information from the server. (default: " + - Mqtt5ConnectRestrictions.DEFAULT_REQUEST_RESPONSE_INFORMATION + ")") - private boolean requestResponseInformation = Mqtt5ConnectRestrictions.DEFAULT_REQUEST_RESPONSE_INFORMATION; + @CommandLine.Option(names = {"--reqResponseInfo"}, + description = "The client requests response information from the server. (default: " + + Mqtt5ConnectRestrictions.DEFAULT_REQUEST_RESPONSE_INFORMATION + + ")", + defaultValue = "false") + private boolean requestResponseInformation; public @Nullable Integer getReceiveMaximum() { return receiveMaximum; @@ -144,10 +158,23 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { @Override public @NotNull String toString() { - return "ConnectRestrictionOptions{" + "receiveMaximum=" + receiveMaximum + ", sendMaximum=" + sendMaximum + - ", maximumPacketSize=" + maximumPacketSize + ", sendMaximumPacketSize=" + sendMaximumPacketSize + - ", topicAliasMaximum=" + topicAliasMaximum + ", sendTopicAliasMaximum=" + sendTopicAliasMaximum + - ", requestProblemInformation=" + requestProblemInformation + ", requestResponseInformation=" + - requestResponseInformation + '}'; + return "ConnectRestrictionOptions{" + + "receiveMaximum=" + + receiveMaximum + + ", sendMaximum=" + + sendMaximum + + ", maximumPacketSize=" + + maximumPacketSize + + ", sendMaximumPacketSize=" + + sendMaximumPacketSize + + ", topicAliasMaximum=" + + topicAliasMaximum + + ", sendTopicAliasMaximum=" + + sendTopicAliasMaximum + + ", requestProblemInformation=" + + requestProblemInformation + + ", requestResponseInformation=" + + requestResponseInformation + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java index e9051a8f0..346640955 100644 --- a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java @@ -16,7 +16,11 @@ package com.hivemq.cli.commands.options; -import com.hivemq.cli.converters.*; +import com.hivemq.cli.converters.ByteBufferConverter; +import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; +import com.hivemq.cli.converters.MqttQosConverter; +import com.hivemq.cli.converters.PayloadFormatIndicatorConverter; +import com.hivemq.cli.converters.UnsignedIntConverter; import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.MqttVersion; import com.hivemq.client.mqtt.datatypes.MqttQos; @@ -38,8 +42,10 @@ public class PublishOptions { private @NotNull String @NotNull [] topics; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) - @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "0", - description = "Quality of service for the corresponding topic (default for all: 0)") + @CommandLine.Option(names = {"-q", "--qos"}, + converter = MqttQosConverter.class, + defaultValue = "0", + description = "Quality of service for the corresponding topic (default for all: 0)") private @NotNull MqttQos @NotNull [] qos; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @@ -47,38 +53,44 @@ public class PublishOptions { private @NotNull MessagePayloadOptions message; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--retain"}, negatable = true, defaultValue = "false", - description = "The message will be retained (default: false)") + @CommandLine.Option(names = {"-r", "--retain"}, + description = "The message will be retained (default: false)", + defaultValue = "false") private boolean retain; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-e", "--messageExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the publish message in seconds (default: no message expiry)") + @CommandLine.Option(names = {"-e", "--messageExpiryInterval"}, + converter = UnsignedIntConverter.class, + description = "The lifetime of the publish message in seconds (default: no message expiry)") private @Nullable Long messageExpiryInterval; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-pf", "--payloadFormatIndicator"}, converter = PayloadFormatIndicatorConverter.class, - description = "The payload format indicator of the publish message") + @CommandLine.Option(names = {"-pf", "--payloadFormatIndicator"}, + converter = PayloadFormatIndicatorConverter.class, + description = "The payload format indicator of the publish message") private @Nullable Mqtt5PayloadFormatIndicator payloadFormatIndicator; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-ct", "--contentType"}, description = "A description of publish message's content", - order = 1) + @CommandLine.Option(names = {"-ct", "--contentType"}, + description = "A description of publish message's content", + order = 1) private @Nullable String contentType; @SuppressWarnings("unused") @CommandLine.Option(names = {"-rt", "--responseTopic"}, - description = "The topic name for the publish message`s response message") + description = "The topic name for the publish message`s response message") private @Nullable String responseTopic; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-cd", "--correlationData"}, converter = ByteBufferConverter.class, - description = "The correlation data of the publish message") + @CommandLine.Option(names = {"-cd", "--correlationData"}, + converter = ByteBufferConverter.class, + description = "The correlation data of the publish message") private @Nullable ByteBuffer correlationData; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the publish message") + @CommandLine.Option(names = {"-up", "--userProperty"}, + converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the publish message") private @Nullable Mqtt5UserProperty @Nullable [] userProperties; public @NotNull String @NotNull [] getTopics() { @@ -122,7 +134,6 @@ public class PublishOptions { } public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { - if (mqttVersion == MqttVersion.MQTT_3_1_1) { if (messageExpiryInterval != null) { Logger.warn("Publish message expiry was set but is unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); @@ -154,11 +165,30 @@ public void arrangeQosToMatchTopics() { @Override public @NotNull String toString() { - return "PublishOptions{" + "topics=" + Arrays.toString(topics) + ", qos=" + Arrays.toString(qos) + - ", message=" + message + ", retain=" + retain + ", messageExpiryInterval=" + messageExpiryInterval + - ", payloadFormatIndicator=" + payloadFormatIndicator + ", contentType='" + contentType + '\'' + - ", responseTopic='" + responseTopic + '\'' + ", correlationData=" + correlationData + - ", userProperties=" + Arrays.toString(userProperties) + '}'; + return "PublishOptions{" + + "topics=" + + Arrays.toString(topics) + + ", qos=" + + Arrays.toString(qos) + + ", message=" + + message + + ", retain=" + + retain + + ", messageExpiryInterval=" + + messageExpiryInterval + + ", payloadFormatIndicator=" + + payloadFormatIndicator + + ", contentType='" + + contentType + + '\'' + + ", responseTopic='" + + responseTopic + + '\'' + + ", correlationData=" + + correlationData + + ", userProperties=" + + Arrays.toString(userProperties) + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java index fb8e2d257..bbe188a14 100644 --- a/src/main/java/com/hivemq/cli/commands/options/WillOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/WillOptions.java @@ -15,7 +15,11 @@ */ package com.hivemq.cli.commands.options; -import com.hivemq.cli.converters.*; +import com.hivemq.cli.converters.ByteBufferConverter; +import com.hivemq.cli.converters.Mqtt5UserPropertyConverter; +import com.hivemq.cli.converters.MqttQosConverter; +import com.hivemq.cli.converters.PayloadFormatIndicatorConverter; +import com.hivemq.cli.converters.UnsignedIntConverter; import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.MqttVersion; import com.hivemq.client.mqtt.datatypes.MqttQos; @@ -38,56 +42,65 @@ public class WillOptions { private @Nullable String willTopic; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Wm", "--willMessage"}, converter = ByteBufferConverter.class, - description = "The payload of the will message") + @CommandLine.Option(names = {"-Wm", "--willMessage"}, + converter = ByteBufferConverter.class, + description = "The payload of the will message") private @Nullable ByteBuffer willMessage; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Wq", "--willQualityOfService"}, defaultValue = "0", - converter = MqttQosConverter.class, - description = "Quality of service level for the will message (default: 0)") + @CommandLine.Option(names = {"-Wq", "--willQualityOfService"}, + defaultValue = "0", + converter = MqttQosConverter.class, + description = "Quality of service level for the will message (default: 0)") private @Nullable MqttQos willQos; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Wr", "--willRetain"}, negatable = true, defaultValue = "false", - description = "Will message as retained message (default: false)") + @CommandLine.Option(names = {"-Wr", "--willRetain"}, + defaultValue = "false", + description = "Will message as retained message (default: false)") private boolean willRetain; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-We", "--willMessageExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the will message in seconds (default: no message expiry)") + @CommandLine.Option(names = {"-We", "--willMessageExpiryInterval"}, + converter = UnsignedIntConverter.class, + description = "The lifetime of the will message in seconds (default: no message expiry)") private @Nullable Long willMessageExpiryInterval; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Wd", "--willDelayInterval"}, converter = UnsignedIntConverter.class, description = - "The Server delays publishing the client's will message until the will delay has passed (default: " + - Mqtt5WillPublish.DEFAULT_DELAY_INTERVAL + ")") + @CommandLine.Option(names = {"-Wd", "--willDelayInterval"}, + converter = UnsignedIntConverter.class, + description = + "The Server delays publishing the client's will message until the will delay has passed (default: " + + Mqtt5WillPublish.DEFAULT_DELAY_INTERVAL + + ")") private @Nullable Long willDelayInterval; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wpf", "--willPayloadFormatIndicator"}, - converter = PayloadFormatIndicatorConverter.class, - description = "The payload format indicator of the will message") + converter = PayloadFormatIndicatorConverter.class, + description = "The payload format indicator of the will message") private @Nullable Mqtt5PayloadFormatIndicator willPayloadFormatIndicator; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wct", "--willContentType"}, - description = "A description of the will message's content") + description = "A description of the will message's content") private @Nullable String willContentType; @SuppressWarnings("unused") @CommandLine.Option(names = {"-Wrt", "--willResponseTopic"}, - description = "The topic name for the response message") + description = "The topic name for the response message") private @Nullable String willResponseTopic; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Wcd", "--willCorrelationData"}, converter = ByteBufferConverter.class, - description = "The correlation data of the will message") + @CommandLine.Option(names = {"-Wcd", "--willCorrelationData"}, + converter = ByteBufferConverter.class, + description = "The correlation data of the will message") private @Nullable ByteBuffer willCorrelationData; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Wup", "--willUserProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the will message") + @CommandLine.Option(names = {"-Wup", "--willUserProperty"}, + converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the will message") private @Nullable Mqtt5UserProperty @Nullable [] willUserProperties; public @Nullable String getWillTopic() { @@ -162,11 +175,32 @@ public void logUnusedOptions(final @NotNull MqttVersion mqttVersion) { @Override public @NotNull String toString() { - return "WillOptions{" + "willTopic='" + willTopic + '\'' + ", willMessage=" + willMessage + ", willQos=" + - willQos + ", willRetain=" + willRetain + ", willMessageExpiryInterval=" + willMessageExpiryInterval + - ", willDelayInterval=" + willDelayInterval + ", willPayloadFormatIndicator=" + - willPayloadFormatIndicator + ", willContentType='" + willContentType + '\'' + ", willResponseTopic='" + - willResponseTopic + '\'' + ", willCorrelationData=" + willCorrelationData + ", willUserProperties=" + - Arrays.toString(willUserProperties) + '}'; + return "WillOptions{" + + "willTopic='" + + willTopic + + '\'' + + ", willMessage=" + + willMessage + + ", willQos=" + + willQos + + ", willRetain=" + + willRetain + + ", willMessageExpiryInterval=" + + willMessageExpiryInterval + + ", willDelayInterval=" + + willDelayInterval + + ", willPayloadFormatIndicator=" + + willPayloadFormatIndicator + + ", willContentType='" + + willContentType + + '\'' + + ", willResponseTopic='" + + willResponseTopic + + '\'' + + ", willCorrelationData=" + + willCorrelationData + + ", willUserProperties=" + + Arrays.toString(willUserProperties) + + '}'; } } From fc2a044a8d4f14da141b7866a0667164b1d6fa0e Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Fri, 16 Dec 2022 16:58:04 +0100 Subject: [PATCH 62/64] Fixed Mixins to pass SystemTests. Fixed native image. Aligned code formatting. --- build.gradle.kts | 16 +- gradle.properties | 6 +- .../cli/TestBrokerCommandDefaultIT.java | 8 +- .../commands/cli/TestBrokerCommandQos0IT.java | 8 +- .../commands/cli/TestBrokerCommandQos1IT.java | 8 +- .../cli/TestBrokerCommandRestrictedIT.java | 8 +- .../clients/ExportClientsCommandIT.java | 10 +- .../swarm/commander/SwarmStatusCommandIT.java | 4 +- .../swarm/run/SwarmRunStartCommandIT.java | 7 +- .../swarm/run/SwarmRunStopCommandIT.java | 27 +- .../test/Mqtt3FeatureTesterDefaultIT.java | 19 +- .../mqtt/test/Mqtt3FeatureTesterQos0IT.java | 5 +- .../mqtt/test/Mqtt3FeatureTesterQos1IT.java | 5 +- .../test/Mqtt3FeatureTesterRestrictedIT.java | 22 +- .../test/Mqtt5FeatureTesterDefaultIT.java | 19 +- .../mqtt/test/Mqtt5FeatureTesterQos0IT.java | 5 +- .../mqtt/test/Mqtt5FeatureTesterQos1IT.java | 5 +- .../test/Mqtt5FeatureTesterRestrictedIT.java | 22 +- .../com/hivemq/cli/DefaultCLIProperties.java | 25 +- src/main/java/com/hivemq/cli/MqttCLIMain.java | 11 +- .../CommandErrorMessageHandler.java | 7 +- .../cli/commandline/CommandLineConfig.java | 9 +- .../CommonErrorMessageHandler.java | 3 +- .../commandline/ShellErrorMessageHandler.java | 4 +- .../hivemq/cli/commands/MqttCLICommand.java | 17 +- .../cli/commands/cli/PublishCommand.java | 29 +- .../cli/commands/cli/SubscribeCommand.java | 41 +- .../cli/commands/cli/TestBrokerCommand.java | 90 ++-- .../cli/commands/hivemq/HiveMQCLICommand.java | 15 +- .../commands/hivemq/export/ExportCommand.java | 15 +- .../clients/ClientDetailsCsvWriterTask.java | 51 ++- .../clients/ClientDetailsRetrieverTask.java | 6 +- .../export/clients/ExportClientsCommand.java | 129 ++++-- .../options/AuthenticationOptions.java | 20 +- .../cli/commands/options/ConnectOptions.java | 73 +++- .../cli/commands/options/DefaultOptions.java | 8 +- .../commands/options/DisconnectOptions.java | 39 +- .../options/MessagePayloadOptions.java | 14 +- .../cli/commands/options/PublishOptions.java | 4 +- .../cli/commands/options/SslOptions.java | 72 +++- .../commands/options/SubscribeOptions.java | 62 ++- .../commands/options/UnsubscribeOptions.java | 8 +- .../commands/shell/ClearScreenCommand.java | 5 +- .../shell/ContextDisconnectCommand.java | 8 +- .../commands/shell/ContextPublishCommand.java | 6 +- .../shell/ContextSubscribeCommand.java | 23 +- .../commands/shell/ContextSwitchCommand.java | 20 +- .../shell/ContextUnsubscribeCommand.java | 6 +- .../commands/shell/ListClientsCommand.java | 59 ++- .../cli/commands/shell/ShellCommand.java | 41 +- .../commands/shell/ShellConnectCommand.java | 16 +- .../commands/shell/ShellContextCommand.java | 16 +- .../shell/ShellDisconnectCommand.java | 13 +- .../cli/commands/shell/VersionCommand.java | 5 +- .../cli/commands/swarm/SwarmCLICommand.java | 15 +- .../swarm/commander/SwarmStatusCommand.java | 53 ++- .../swarm/error/SwarmApiErrorTransformer.java | 3 +- .../cli/commands/swarm/run/SwarmOptions.java | 10 +- .../commands/swarm/run/SwarmRunCommand.java | 15 +- .../swarm/run/SwarmRunStartCommand.java | 58 ++- .../swarm/run/SwarmRunStopCommand.java | 30 +- .../converters/FileToPrivateKeyConverter.java | 8 +- .../Mqtt5UserPropertyConverter.java | 2 - .../PasswordFileToByteBufferConverter.java | 6 +- .../hivemq/cli/graal/BouncyCastleFeature.java | 37 ++ .../java/com/hivemq/cli/graal/NativeMain.java | 19 +- src/main/java/com/hivemq/cli/ioc/MqttCLI.java | 3 +- .../java/com/hivemq/cli/ioc/ShellModule.java | 15 +- .../cli/mqtt/AbstractMqttClientExecutor.java | 68 +-- .../java/com/hivemq/cli/mqtt/ClientKey.java | 26 +- .../mqtt/ContextClientDisconnectListener.java | 10 +- .../hivemq/cli/mqtt/MqttClientExecutor.java | 76 ++-- .../mqtt/SubscribeMqtt3PublishCallback.java | 3 +- .../mqtt/SubscribeMqtt5PublishCallback.java | 3 +- .../cli/mqtt/test/Mqtt3FeatureTester.java | 19 +- .../cli/mqtt/test/Mqtt5FeatureTester.java | 37 +- .../hivemq/cli/rest/HiveMQRestService.java | 6 +- .../hivemq/cli/utils/IntersectionUtil.java | 18 +- .../com/hivemq/cli/utils/LoggerUtils.java | 6 +- .../hivemq/cli/utils/MqttPublishUtils.java | 3 +- .../java/com/hivemq/cli/utils/MqttUtils.java | 23 +- .../META-INF/native-image/reflect-config.json | 394 +++++++++++++++--- .../cli/publish/PublishConnectST.java | 60 ++- .../cli/commands/cli/publish/PublishST.java | 4 +- .../cli/subscribe/SubscribeConnectST.java | 47 ++- .../commands/cli/subscribe/SubscribeST.java | 2 +- .../shell/connect/ShellConnectST.java | 78 +++- .../cli/utils/OrphanProcessCleanup.java | 2 +- .../resources/OrphanCleanupProcess.java | 18 +- 89 files changed, 1668 insertions(+), 683 deletions(-) create mode 100644 src/main/java/com/hivemq/cli/graal/BouncyCastleFeature.java diff --git a/build.gradle.kts b/build.gradle.kts index 0dcee5fa0..a4561fe21 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,8 +102,8 @@ dependencies { implementation("org.tinylog:tinylog-api:${property("tinylog.version")}") implementation("org.tinylog:tinylog-impl:${property("tinylog.version")}") implementation("org.jetbrains:annotations:${property("jetbrains-annotations.version")}") - implementation("org.bouncycastle:bcprov-jdk15on:${property("bouncycastle.version")}") - implementation("org.bouncycastle:bcpkix-jdk15on:${property("bouncycastle.version")}") + implementation("org.bouncycastle:bcprov-jdk18on:${property("bouncycastle.version")}") + implementation("org.bouncycastle:bcpkix-jdk18on:${property("bouncycastle.version")}") implementation("com.hivemq:hivemq-mqtt-client:${property("hivemq-client.version")}") implementation("io.netty:netty-handler:${property("netty.version")}") implementation("io.netty:netty-codec-http:${property("netty.version")}") @@ -111,8 +111,10 @@ dependencies { implementation("com.opencsv:opencsv:${property("open-csv.version")}") constraints { implementation("org.apache.commons:commons-text:1.10.0") { - because("Force a commons-text version that does not contain CVE-2022-42889, " + - "because opencsv brings the vulnerable version 1.9 as transitive dependency") + because( + "Force a commons-text version that does not contain CVE-2022-42889, " + + "because opencsv brings the vulnerable version 1.9 as transitive dependency" + ) } } } @@ -503,8 +505,14 @@ val nativeImageOptions by graalvmNative.binaries.named("main") { buildArgs.add("-H:+TraceServiceLoaderFeature") buildArgs.add("--no-fallback") buildArgs.add("--enable-https") + buildArgs.add( + "--rerun-class-initialization-at-runtime=" + + "org.bouncycastle.jcajce.provider.drbg.DRBG\$Default," + + "org.bouncycastle.jcajce.provider.drbg.DRBG\$NonceAndIV" + ) buildArgs.add( "--initialize-at-build-time=" + + "org.bouncycastle," + "org.jctools.queues.BaseMpscLinkedArrayQueue," + "org.jctools.queues.BaseSpscLinkedArrayQueue," + "org.jctools.util.UnsafeAccess," + diff --git a/gradle.properties b/gradle.properties index 5092b4bc0..644969044 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ # # dependencies # -bouncycastle.version=1.70 +bouncycastle.version=1.72 commons-io.version=2.11.0 commons-lang.version=3.12.0 dagger.version=2.43.2 @@ -40,7 +40,7 @@ awaitility.version=4.2.0 # # native image # -graal.version=22.2.0 +graal.version=22.3.0 java-native.version=17 # # plugins @@ -50,7 +50,7 @@ plugin.defaults.version=0.2.0 plugin.forbiddenapis.version=3.3 plugin.git-publish.version=3.0.0 plugin.github-release.version=2.4.1 -plugin.graal.version=0.9.13 +plugin.graal.version=0.9.19 plugin.jib.version=3.2.1 plugin.launch4j.version=2.5.2 plugin.license.version=0.16.1 diff --git a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandDefaultIT.java b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandDefaultIT.java index dd07d065d..1a0006c9d 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandDefaultIT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandDefaultIT.java @@ -21,7 +21,11 @@ import com.hivemq.cli.utils.TestLoggerUtils; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.testcontainers.utility.DockerImageName; @Disabled("Tests are only used to check output") @@ -68,4 +72,4 @@ void mqtt3_features() { void mqtt5_features() { MqttCLIMain.main("test", "-V", "5", "-a", "-p", String.valueOf(hivemq.getMqttPort())); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos0IT.java b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos0IT.java index 192741d54..5535dbba6 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos0IT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos0IT.java @@ -21,7 +21,11 @@ import com.hivemq.cli.utils.TestLoggerUtils; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; @@ -58,4 +62,4 @@ void qos0_restricted_mqtt3_features() { void qos0_restricted_mqtt5_features() { MqttCLIMain.main("test", "-V", "3", "-p", String.valueOf(hivemq.getMqttPort())); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos1IT.java b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos1IT.java index ac563c97c..e7980e1da 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos1IT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandQos1IT.java @@ -21,7 +21,11 @@ import com.hivemq.cli.utils.TestLoggerUtils; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; @@ -58,4 +62,4 @@ void qos1_restricted_mqtt3_features() { void qos1_restricted_mqtt5_features() { MqttCLIMain.main("test", "-V", "5", "-a", "-p", String.valueOf(hivemq.getMqttPort())); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandRestrictedIT.java b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandRestrictedIT.java index 37667b21a..cbf8ed0d5 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandRestrictedIT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/cli/TestBrokerCommandRestrictedIT.java @@ -21,7 +21,11 @@ import com.hivemq.cli.utils.TestLoggerUtils; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; @@ -58,4 +62,4 @@ void restricted_mqtt3_features() { void restricted_mqtt5_features() { MqttCLIMain.main("test", "-V", "5", "-a", "-p", String.valueOf(hivemq.getMqttPort())); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommandIT.java b/src/integrationTest/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommandIT.java index 10532628a..4c714090b 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommandIT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommandIT.java @@ -48,8 +48,8 @@ class ExportClientsCommandIT { @RegisterExtension final @NotNull HiveMQTestContainerExtension hivemq = - new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")).withHiveMQConfig( - MountableFile.forClasspathResource("hivemq.configs/rest-api-config.xml")) + new HiveMQTestContainerExtension(DockerImageName.parse("hivemq/hivemq4")).withHiveMQConfig(MountableFile.forClasspathResource( + "hivemq.configs/rest-api-config.xml")) .withExposedPorts(HiveMQTestContainerExtension.MQTT_PORT, HTTP_PORT); private @NotNull File file; @@ -167,11 +167,11 @@ void connection_refused() { client.connect(); final CommandLine cmd = new CommandLine(new ExportClientsCommand()); - final int returnCode = cmd.execute("-f=" + file.getAbsolutePath(), - "-url=http://" + hivemq.getHost() + ":" + 8889); + final int returnCode = + cmd.execute("-f=" + file.getAbsolutePath(), "-url=http://" + hivemq.getHost() + ":" + 8889); assertEquals(-1, returnCode); client.disconnect(); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommandIT.java b/src/integrationTest/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommandIT.java index 3f6c13a7c..98edc042a 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommandIT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommandIT.java @@ -95,8 +95,8 @@ void tearDown() { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void getCommanderStatus() { - final int execute = commandLine.execute( - "-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT)); + final int execute = + commandLine.execute("-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT)); assertEquals(0, execute); verify(out).println("Status: READY"); } diff --git a/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommandIT.java b/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommandIT.java index febd76bb0..a79009377 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommandIT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommandIT.java @@ -44,7 +44,9 @@ import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; public class SwarmRunStartCommandIT { @@ -115,8 +117,7 @@ void startScenario() throws Exception { final String scenario = MountableFile.forClasspathResource("SwarmRunStartCommandIT/my-scenario.xml").getResolvedPath(); - final int execute = commandLine.execute( - "-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT), + final int execute = commandLine.execute("-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT), "-f=" + scenario); assertEquals(0, execute); diff --git a/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommandIT.java b/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommandIT.java index d12f7c0b1..2a44886f0 100644 --- a/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommandIT.java +++ b/src/integrationTest/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommandIT.java @@ -20,7 +20,14 @@ import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; import com.hivemq.cli.openapi.ApiClient; import com.hivemq.cli.openapi.Configuration; -import com.hivemq.cli.openapi.swarm.*; +import com.hivemq.cli.openapi.swarm.CommanderApi; +import com.hivemq.cli.openapi.swarm.CommanderStateResponse; +import com.hivemq.cli.openapi.swarm.RunResponse; +import com.hivemq.cli.openapi.swarm.RunsApi; +import com.hivemq.cli.openapi.swarm.ScenariosApi; +import com.hivemq.cli.openapi.swarm.StartRunRequest; +import com.hivemq.cli.openapi.swarm.UploadScenarioRequest; +import com.hivemq.cli.openapi.swarm.UploadScenarioResponse; import com.hivemq.cli.utils.TestLoggerUtils; import com.hivemq.client.mqtt.MqttGlobalPublishFilter; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; @@ -50,7 +57,9 @@ import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; public class SwarmRunStopCommandIT { @@ -127,8 +136,8 @@ void tearDown() { @Timeout(value = 3, unit = TimeUnit.MINUTES) void stopRun() throws Exception { // no current run - final int execute0 = commandLine.execute( - "-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT)); + final int execute0 = + commandLine.execute("-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT)); assertEquals(0, execute0); verify(out, times(1)).println("No run in progress."); @@ -153,9 +162,8 @@ void stopRun() throws Exception { // stop the scenario //TODO: not sure why here a logger reset is necessary (local machine) TestLoggerUtils.resetLogger(); - final int execute = commandLine.execute( - "-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT), - "-r" + 1); + final int execute = + commandLine.execute("-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT), "-r" + 1); assertEquals(0, execute); await().atMost(Duration.ofSeconds(3)).until(() -> { @@ -170,9 +178,8 @@ void stopRun() throws Exception { // stop a run that does not exist //TODO: however here a logger reset is not necessary (local machine) //TestLoggerUtils.resetLogger(); - final int execute2 = commandLine.execute( - "-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT), - "-r" + 1); + final int execute2 = + commandLine.execute("-url=http://" + swarm.getHost() + ":" + swarm.getMappedPort(REST_PORT), "-r" + 1); assertEquals(-1, execute2); } } diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterDefaultIT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterDefaultIT.java index 6a7a68dae..e3b65c139 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterDefaultIT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterDefaultIT.java @@ -16,7 +16,14 @@ package com.hivemq.cli.mqtt.test; -import com.hivemq.cli.mqtt.test.results.*; +import com.hivemq.cli.mqtt.test.results.AsciiCharsInClientIdTestResults; +import com.hivemq.cli.mqtt.test.results.ClientIdLengthTestResults; +import com.hivemq.cli.mqtt.test.results.PayloadTestResults; +import com.hivemq.cli.mqtt.test.results.QosTestResult; +import com.hivemq.cli.mqtt.test.results.SharedSubscriptionTestResult; +import com.hivemq.cli.mqtt.test.results.TestResult; +import com.hivemq.cli.mqtt.test.results.TopicLengthTestResults; +import com.hivemq.cli.mqtt.test.results.WildcardSubscriptionsTestResult; import com.hivemq.cli.utils.Tuple; import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; @@ -32,7 +39,10 @@ import static com.hivemq.cli.mqtt.test.results.TestResult.OK; import static com.hivemq.client.mqtt.mqtt3.message.connect.connack.Mqtt3ConnAckReturnCode.SUCCESS; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class Mqtt3FeatureTesterDefaultIT { @@ -48,8 +58,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt3FeatureTester = - new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt3FeatureTester = new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -146,4 +155,4 @@ void asciiChars_success() { } assertTrue(asciiCharsInClientIdTestResults.getUnsupportedChars().isEmpty()); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos0IT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos0IT.java index 99c413e19..f742539f6 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos0IT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos0IT.java @@ -44,8 +44,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt3FeatureTester = - new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt3FeatureTester = new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -70,4 +69,4 @@ void qos_2_failed() { final QosTestResult qosTestResult = mqtt3FeatureTester.testQos(MqttQos.EXACTLY_ONCE, 10); assertEquals(0, qosTestResult.getReceivedPublishes()); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos1IT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos1IT.java index 56c656d3f..8b7837ca6 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos1IT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterQos1IT.java @@ -44,8 +44,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt3FeatureTester = - new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt3FeatureTester = new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -70,4 +69,4 @@ void qos_2_failed() { final QosTestResult qosTestResult = mqtt3FeatureTester.testQos(MqttQos.EXACTLY_ONCE, 10); assertEquals(0, qosTestResult.getReceivedPublishes()); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterRestrictedIT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterRestrictedIT.java index a3947431e..02a3108d1 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterRestrictedIT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTesterRestrictedIT.java @@ -16,17 +16,28 @@ package com.hivemq.cli.mqtt.test; -import com.hivemq.cli.mqtt.test.results.*; +import com.hivemq.cli.mqtt.test.results.ClientIdLengthTestResults; +import com.hivemq.cli.mqtt.test.results.PayloadTestResults; +import com.hivemq.cli.mqtt.test.results.SharedSubscriptionTestResult; +import com.hivemq.cli.mqtt.test.results.TestResult; +import com.hivemq.cli.mqtt.test.results.TopicLengthTestResults; +import com.hivemq.cli.mqtt.test.results.WildcardSubscriptionsTestResult; import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import static com.hivemq.cli.mqtt.test.results.TestResult.PUBLISH_FAILED; import static com.hivemq.cli.mqtt.test.results.TestResult.SUBSCRIBE_FAILED; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class Mqtt3FeatureTesterRestrictedIT { @@ -43,8 +54,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt3FeatureTester = - new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt3FeatureTester = new Mqtt3FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -97,4 +107,4 @@ void clientId_length_failed_max_30() { final ClientIdLengthTestResults clientIdLengthTestResults = mqtt3FeatureTester.testClientIdLength(); assertEquals(30, clientIdLengthTestResults.getMaxClientIdLength()); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterDefaultIT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterDefaultIT.java index fc7c32499..b26e3c114 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterDefaultIT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterDefaultIT.java @@ -16,7 +16,14 @@ package com.hivemq.cli.mqtt.test; -import com.hivemq.cli.mqtt.test.results.*; +import com.hivemq.cli.mqtt.test.results.AsciiCharsInClientIdTestResults; +import com.hivemq.cli.mqtt.test.results.ClientIdLengthTestResults; +import com.hivemq.cli.mqtt.test.results.PayloadTestResults; +import com.hivemq.cli.mqtt.test.results.QosTestResult; +import com.hivemq.cli.mqtt.test.results.SharedSubscriptionTestResult; +import com.hivemq.cli.mqtt.test.results.TestResult; +import com.hivemq.cli.mqtt.test.results.TopicLengthTestResults; +import com.hivemq.cli.mqtt.test.results.WildcardSubscriptionsTestResult; import com.hivemq.cli.utils.Tuple; import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.exceptions.ConnectionFailedException; @@ -31,7 +38,10 @@ import org.testcontainers.utility.DockerImageName; import static com.hivemq.cli.mqtt.test.results.TestResult.OK; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class Mqtt5FeatureTesterDefaultIT { @@ -47,8 +57,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt5FeatureTester = - new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt5FeatureTester = new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -145,4 +154,4 @@ void asciiChars_success() { } assertTrue(asciiCharsInClientIdTestResults.getUnsupportedChars().isEmpty()); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos0IT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos0IT.java index 19fca444f..74ff41ef5 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos0IT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos0IT.java @@ -44,8 +44,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt5FeatureTester = - new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt5FeatureTester = new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -70,4 +69,4 @@ void qos_2_failed() { final QosTestResult qosTestResult = mqtt5FeatureTester.testQos(MqttQos.EXACTLY_ONCE, 10); assertEquals(0, qosTestResult.getReceivedPublishes()); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos1IT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos1IT.java index bf64afbae..296d30e0b 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos1IT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterQos1IT.java @@ -44,8 +44,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt5FeatureTester = - new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt5FeatureTester = new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -70,4 +69,4 @@ void qos_2_failed() { final QosTestResult qosTestResult = mqtt5FeatureTester.testQos(MqttQos.EXACTLY_ONCE, 10); assertEquals(0, qosTestResult.getReceivedPublishes()); } -} \ No newline at end of file +} diff --git a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterRestrictedIT.java b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterRestrictedIT.java index 6824930fc..9a94273f7 100644 --- a/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterRestrictedIT.java +++ b/src/integrationTest/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTesterRestrictedIT.java @@ -16,17 +16,28 @@ package com.hivemq.cli.mqtt.test; -import com.hivemq.cli.mqtt.test.results.*; +import com.hivemq.cli.mqtt.test.results.ClientIdLengthTestResults; +import com.hivemq.cli.mqtt.test.results.PayloadTestResults; +import com.hivemq.cli.mqtt.test.results.SharedSubscriptionTestResult; +import com.hivemq.cli.mqtt.test.results.TestResult; +import com.hivemq.cli.mqtt.test.results.TopicLengthTestResults; +import com.hivemq.cli.mqtt.test.results.WildcardSubscriptionsTestResult; import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.testcontainer.junit5.HiveMQTestContainerExtension; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import static com.hivemq.cli.mqtt.test.results.TestResult.PUBLISH_FAILED; import static com.hivemq.cli.mqtt.test.results.TestResult.SUBSCRIBE_FAILED; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class Mqtt5FeatureTesterRestrictedIT { @@ -43,8 +54,7 @@ static void beforeAll() { @BeforeEach void setUp() { - mqtt5FeatureTester = - new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); + mqtt5FeatureTester = new Mqtt5FeatureTester(hivemq.getHost(), hivemq.getMqttPort(), null, null, null, 3); } @AfterAll @@ -95,4 +105,4 @@ void clientId_length_failed_max_30() { final ClientIdLengthTestResults clientIdLengthTestResults = mqtt5FeatureTester.testClientIdLength(); assertEquals(30, clientIdLengthTestResults.getMaxClientIdLength()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/hivemq/cli/DefaultCLIProperties.java b/src/main/java/com/hivemq/cli/DefaultCLIProperties.java index 969249075..9363219bc 100644 --- a/src/main/java/com/hivemq/cli/DefaultCLIProperties.java +++ b/src/main/java/com/hivemq/cli/DefaultCLIProperties.java @@ -26,8 +26,12 @@ import org.tinylog.Level; import javax.inject.Singleton; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; +import java.nio.file.Files; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Collection; @@ -70,9 +74,12 @@ public class DefaultCLIProperties { put(CLIENT_ID_PREFIX, "mqtt"); put(CLIENT_ID_LENGTH, "8"); put(SUBSCRIBE_OUTPUT_FILE, null); - put( - LOGFILE_PATH, - System.getProperty("user.home") + File.separator + ".mqtt-cli" + File.separator + "logs" + + put(LOGFILE_PATH, + System.getProperty("user.home") + + File.separator + + ".mqtt-cli" + + File.separator + + "logs" + File.separator); put(USERNAME, null); put(PASSWORD, null); @@ -108,9 +115,9 @@ void init() throws IOException, IllegalArgumentException { if (!storePropertiesFile.exists()) { createFile(); } else if (!storePropertiesFile.isFile()) { - throw new IllegalArgumentException( - "The given file path does not lead to a valid properties file ('" + storePropertiesFile.getPath() + - "')"); + throw new IllegalArgumentException("The given file path does not lead to a valid properties file ('" + + storePropertiesFile.getPath() + + "')"); } else { readFromFile(); } @@ -119,7 +126,7 @@ void init() throws IOException, IllegalArgumentException { private void readFromFile() throws IOException { final Properties fileProperties = new Properties(); - try (final InputStream input = new FileInputStream(storePropertiesFile)) { + try (final InputStream input = Files.newInputStream(storePropertiesFile.toPath())) { fileProperties.load(input); } @@ -133,7 +140,7 @@ private void readFromFile() throws IOException { private void createFile() throws IOException { storePropertiesFile.getParentFile().mkdirs(); assert storePropertiesFile.createNewFile(); - try (final OutputStream output = new FileOutputStream(storePropertiesFile)) { + try (final OutputStream output = Files.newOutputStream(storePropertiesFile.toPath())) { final Properties properties = getProperties(); properties.store(output, null); } diff --git a/src/main/java/com/hivemq/cli/MqttCLIMain.java b/src/main/java/com/hivemq/cli/MqttCLIMain.java index 74c5859b1..c7fcde63d 100644 --- a/src/main/java/com/hivemq/cli/MqttCLIMain.java +++ b/src/main/java/com/hivemq/cli/MqttCLIMain.java @@ -19,6 +19,7 @@ import com.hivemq.cli.ioc.DaggerMqttCLI; import com.hivemq.cli.ioc.MqttCLI; import com.hivemq.cli.mqtt.ClientData; +import com.hivemq.cli.mqtt.ClientKey; import com.hivemq.cli.mqtt.MqttClientExecutor; import com.hivemq.client.mqtt.MqttClient; import com.hivemq.client.mqtt.mqtt3.Mqtt3Client; @@ -69,11 +70,11 @@ private static class DisconnectAllClientsTask extends Thread { @Override public void run() { - final Map clientKeyToClientData = MqttClientExecutor.getClientDataMap(); + final Map clientKeyToClientData = MqttClientExecutor.getClientDataMap(); final List> disconnectFutures = new ArrayList<>(); - for (final Map.Entry entry : clientKeyToClientData.entrySet()) { + for (final Map.Entry entry : clientKeyToClientData.entrySet()) { final MqttClient client = entry.getValue().getClient(); if (client.getConfig().getState().isConnectedOrReconnect()) { switch (client.getConfig().getMqttVersion()) { @@ -99,10 +100,10 @@ public static class CLIVersionProvider implements CommandLine.IVersionProvider { version = "DEVELOPMENT"; } return new String[]{ - version, "Picocli " + CommandLine.VERSION, + version, + "Picocli " + CommandLine.VERSION, "JVM: ${java.version} (${java.vendor} ${java.vm.name} ${java.vm.version})", - "OS: ${os.name} ${os.version} ${os.arch}" - }; + "OS: ${os.name} ${os.version} ${os.arch}"}; } } } diff --git a/src/main/java/com/hivemq/cli/commandline/CommandErrorMessageHandler.java b/src/main/java/com/hivemq/cli/commandline/CommandErrorMessageHandler.java index d4495645d..cda7ed380 100644 --- a/src/main/java/com/hivemq/cli/commandline/CommandErrorMessageHandler.java +++ b/src/main/java/com/hivemq/cli/commandline/CommandErrorMessageHandler.java @@ -26,20 +26,17 @@ public class CommandErrorMessageHandler extends CommonErrorMessageHandler implements CommandLine.IParameterExceptionHandler { @Inject - CommandErrorMessageHandler() {} + CommandErrorMessageHandler() { + } @Override public int handleParseException( final @NotNull CommandLine.ParameterException ex, final @NotNull String @NotNull [] args) throws Exception { final int exitCode = super.handleParseException(ex, args); - final CommandLine cmd = ex.getCommandLine(); final PrintWriter writer = cmd.getErr(); - final CommandLine.Model.CommandSpec spec = cmd.getCommandSpec(); - writer.printf("Try '%s --help' for more information.%n", spec.qualifiedName()); - return exitCode; } } diff --git a/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java b/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java index fb1e39acd..81eb6ee14 100644 --- a/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java +++ b/src/main/java/com/hivemq/cli/commandline/CommandLineConfig.java @@ -25,19 +25,18 @@ @Singleton public class CommandLineConfig { - //@formatter:off private final static @NotNull CommandLine.Help.ColorScheme COLOR_SCHEME = - new CommandLine.Help.ColorScheme.Builder(CommandLine.Help.Ansi.AUTO) - .commands(CommandLine.Help.Ansi.Style.bold, CommandLine.Help.Ansi.Style.fg_yellow) + new CommandLine.Help.ColorScheme.Builder(CommandLine.Help.Ansi.AUTO).commands(CommandLine.Help.Ansi.Style.bold, + CommandLine.Help.Ansi.Style.fg_yellow) .options(CommandLine.Help.Ansi.Style.italic, CommandLine.Help.Ansi.Style.fg_yellow) .parameters(CommandLine.Help.Ansi.Style.fg_yellow) .optionParams(CommandLine.Help.Ansi.Style.italic) .build(); - //@formatter:on private static final int CLI_WIDTH = 160; @Inject - public CommandLineConfig() {} + public CommandLineConfig() { + } public @NotNull CommandLine.Help.ColorScheme getColorScheme() { return COLOR_SCHEME; diff --git a/src/main/java/com/hivemq/cli/commandline/CommonErrorMessageHandler.java b/src/main/java/com/hivemq/cli/commandline/CommonErrorMessageHandler.java index 0f38e73db..db00c9e7d 100644 --- a/src/main/java/com/hivemq/cli/commandline/CommonErrorMessageHandler.java +++ b/src/main/java/com/hivemq/cli/commandline/CommonErrorMessageHandler.java @@ -46,6 +46,7 @@ private int generateExitCode(final @NotNull CommandLine.ParameterException param final CommandLine cmd = parameterException.getCommandLine(); final CommandLine.Model.CommandSpec spec = cmd.getCommandSpec(); return cmd.getExitCodeExceptionMapper() != null ? - cmd.getExitCodeExceptionMapper().getExitCode(parameterException) : spec.exitCodeOnInvalidInput(); + cmd.getExitCodeExceptionMapper().getExitCode(parameterException) : + spec.exitCodeOnInvalidInput(); } } diff --git a/src/main/java/com/hivemq/cli/commandline/ShellErrorMessageHandler.java b/src/main/java/com/hivemq/cli/commandline/ShellErrorMessageHandler.java index 9cac550bd..d1dc76b6c 100644 --- a/src/main/java/com/hivemq/cli/commandline/ShellErrorMessageHandler.java +++ b/src/main/java/com/hivemq/cli/commandline/ShellErrorMessageHandler.java @@ -26,13 +26,13 @@ public class ShellErrorMessageHandler extends CommonErrorMessageHandler implements CommandLine.IParameterExceptionHandler { @Inject - ShellErrorMessageHandler() {} + ShellErrorMessageHandler() { + } @Override public int handleParseException( final @NotNull CommandLine.ParameterException ex, final @NotNull String @NotNull [] args) throws Exception { final int exitCode = super.handleParseException(ex, args); - final PrintWriter writer = ex.getCommandLine().getErr(); if (ex instanceof CommandLine.UnmatchedArgumentException && diff --git a/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java b/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java index f6521326c..d50802cfd 100644 --- a/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/MqttCLICommand.java @@ -22,18 +22,23 @@ import javax.inject.Inject; -@CommandLine.Command(name = "mqtt", description = "MQTT Command Line Interpreter.", - synopsisHeading = "%n@|bold Usage:|@ ", - synopsisSubcommandLabel = "{ pub | sub | shell | test | hivemq | swarm }", descriptionHeading = "%n", - optionListHeading = "%n@|bold Options:|@%n", commandListHeading = "%n@|bold Commands:|@%n", - versionProvider = MqttCLIMain.CLIVersionProvider.class, mixinStandardHelpOptions = true) +@CommandLine.Command(name = "mqtt", + description = "MQTT Command Line Interpreter.", + synopsisHeading = "%n@|bold Usage:|@ ", + synopsisSubcommandLabel = "{ pub | sub | shell | test | hivemq | swarm }", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class MqttCLICommand { @SuppressWarnings("unused") public static final @NotNull String VERSION_STRING = "1.0"; @Inject - MqttCLICommand() {} + MqttCLICommand() { + } @Override public @NotNull String toString() { diff --git a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java index 59bb13c68..c4ca77431 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java @@ -31,13 +31,16 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "pub", versionProvider = MqttCLIMain.CLIVersionProvider.class, aliases = "publish", - description = "Publish a message to a list of topics.") +@CommandLine.Command(name = "pub", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + aliases = "publish", + description = "Publish a message to a list of topics.") public class PublishCommand implements Callable { @SuppressWarnings("unused") - @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") + @CommandLine.Option(names = {"-l"}, + defaultValue = "false", + description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; @CommandLine.Mixin @@ -61,7 +64,6 @@ public PublishCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @Override public @NotNull Integer call() { - String logLevel = "warn"; if (debugOptions.isDebug()) { logLevel = "debug"; @@ -98,8 +100,19 @@ public PublishCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { @Override public @NotNull String toString() { - return "PublishCommand{" + "logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + - ", publishOptions=" + publishOptions + ", debugOptions=" + debugOptions + ", defaultOptions=" + - defaultOptions + ", mqttClientExecutor=" + mqttClientExecutor + '}'; + return "PublishCommand{" + + "logToLogfile=" + + logToLogfile + + ", connectOptions=" + + connectOptions + + ", publishOptions=" + + publishOptions + + ", debugOptions=" + + debugOptions + + ", defaultOptions=" + + defaultOptions + + ", mqttClientExecutor=" + + mqttClientExecutor + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java index 0cdfb1331..c3abb5a82 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/SubscribeCommand.java @@ -34,8 +34,10 @@ import java.util.Objects; import java.util.concurrent.Callable; -@CommandLine.Command(name = "sub", versionProvider = MqttCLIMain.CLIVersionProvider.class, aliases = "subscribe", - description = "Subscribe an MQTT client to a list of topics.") +@CommandLine.Command(name = "sub", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + aliases = "subscribe", + description = "Subscribe an MQTT client to a list of topics.") public class SubscribeCommand implements Callable { private static final int IDLE_TIME = 5000; @@ -43,10 +45,21 @@ public class SubscribeCommand implements Callable { private @Nullable MqttClient subscribeClient; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") + @CommandLine.Option(names = {"-l"}, + defaultValue = "false", + description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-no-oc", "--no-outputToConsole"}, + hidden = true, + negatable = true, + defaultValue = "true", + description = "The received messages will be written to the console (default: true)") + private void printToSTDOUT(final boolean printToSTDOUT) { + subscribeOptions.setPrintToSTDOUT(printToSTDOUT); + } + @CommandLine.Mixin private final @NotNull ConnectOptions connectOptions = new ConnectOptions(); @@ -119,9 +132,21 @@ private void stay() throws InterruptedException { @Override public @NotNull String toString() { - return "SubscribeCommand{" + "mqttClientExecutor=" + mqttClientExecutor + ", subscribeClient=" + - subscribeClient + ", logToLogfile=" + logToLogfile + ", connectOptions=" + connectOptions + - ", subscribeOptions=" + subscribeOptions + ", debugOptions=" + debugOptions + ", defaultOptions=" + - defaultOptions + '}'; + return "SubscribeCommand{" + + "mqttClientExecutor=" + + mqttClientExecutor + + ", subscribeClient=" + + subscribeClient + + ", logToLogfile=" + + logToLogfile + + ", connectOptions=" + + connectOptions + + ", subscribeOptions=" + + subscribeOptions + + ", debugOptions=" + + debugOptions + + ", defaultOptions=" + + defaultOptions + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java b/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java index cd8c6e96c..a8fb53bf8 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/TestBrokerCommand.java @@ -25,7 +25,13 @@ import com.hivemq.cli.converters.MqttVersionConverter; import com.hivemq.cli.mqtt.test.Mqtt3FeatureTester; import com.hivemq.cli.mqtt.test.Mqtt5FeatureTester; -import com.hivemq.cli.mqtt.test.results.*; +import com.hivemq.cli.mqtt.test.results.AsciiCharsInClientIdTestResults; +import com.hivemq.cli.mqtt.test.results.ClientIdLengthTestResults; +import com.hivemq.cli.mqtt.test.results.PayloadTestResults; +import com.hivemq.cli.mqtt.test.results.QosTestResult; +import com.hivemq.cli.mqtt.test.results.SharedSubscriptionTestResult; +import com.hivemq.cli.mqtt.test.results.TopicLengthTestResults; +import com.hivemq.cli.mqtt.test.results.WildcardSubscriptionsTestResult; import com.hivemq.cli.utils.LoggerUtils; import com.hivemq.client.mqtt.MqttClientSslConfig; import com.hivemq.client.mqtt.MqttVersion; @@ -46,44 +52,47 @@ import java.util.concurrent.Callable; @CommandLine.Command(name = "test", - description = "Tests the specified broker on different MQTT feature support and prints the results.", - sortOptions = false) + description = "Tests the specified broker on different MQTT feature support and prints the results.", + sortOptions = false) public class TestBrokerCommand implements Callable { private static final int MAX_PAYLOAD_TEST_SIZE = 100000; // ~ 1 MB @CommandLine.Option(names = {"-h", "--host"}, - description = "The hostname of the message broker (default 'localhost')", order = 1) + description = "The hostname of the message broker (default 'localhost')") private @Nullable String host; - @CommandLine.Option(names = {"-p", "--port"}, description = "The port of the message broker (default: 1883)", - order = 1) + @CommandLine.Option(names = {"-p", "--port"}, description = "The port of the message broker (default: 1883)") private @Nullable Integer port; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-V", "--mqttVersion"}, converter = MqttVersionConverter.class, - description = "The MQTT version to test the broker on (default: test both versions)", order = 1) + @CommandLine.Option(names = {"-V", "--mqttVersion"}, + converter = MqttVersionConverter.class, + description = "The MQTT version to test the broker on (default: test both versions)") private @Nullable MqttVersion version; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-a", "--all"}, defaultValue = "false", - description = "Perform all tests for all MQTT versions (default: only MQTT 3)", order = 1) + @CommandLine.Option(names = {"-a", "--all"}, + defaultValue = "false", + description = "Perform all tests for all MQTT versions (default: only MQTT 3)") private boolean testAll; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-t", "--timeOut"}, defaultValue = "10", - description = "The time to wait for the broker to respond", order = 1) + @CommandLine.Option(names = {"-t", "--timeOut"}, + defaultValue = "10", + description = "The time to wait for the broker to respond") private @NotNull Integer timeOut; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-q", "--qosTries"}, defaultValue = "10", - description = "The amount of publishes to send to the broker on every qos level", order = 1) + @CommandLine.Option(names = {"-q", "--qosTries"}, + defaultValue = "10", + description = "The amount of publishes to send to the broker on every qos level") private @NotNull Integer qosTries; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", - order = 1) + @CommandLine.Option(names = {"-l"}, + defaultValue = "false", + description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; @CommandLine.Mixin @@ -140,8 +149,7 @@ public TestBrokerCommand(final @NotNull DefaultCLIProperties defaultCLIPropertie } public void testMqtt5Features() { - final Mqtt5FeatureTester mqtt5Tester = new Mqtt5FeatureTester( - Objects.requireNonNull(host), + final Mqtt5FeatureTester mqtt5Tester = new Mqtt5FeatureTester(Objects.requireNonNull(host), Objects.requireNonNull(port), authenticationOptions.getUser(), authenticationOptions.getPassword(), @@ -201,12 +209,13 @@ public void testMqtt5Features() { System.out.println(restrictions.getTopicAliasMaximum()); System.out.print("\t\t> Session expiry interval: "); - System.out.println( - connAck.getSessionExpiryInterval().isPresent() ? connAck.getSessionExpiryInterval().getAsLong() + "s" : - "Client-based"); + System.out.println(connAck.getSessionExpiryInterval().isPresent() ? + connAck.getSessionExpiryInterval().getAsLong() + "s" : + "Client-based"); System.out.print("\t\t> Server keep alive: "); - System.out.println(connAck.getServerKeepAlive().isPresent() ? connAck.getServerKeepAlive().getAsInt() + "s" : + System.out.println(connAck.getServerKeepAlive().isPresent() ? + connAck.getServerKeepAlive().getAsInt() + "s" : "Client-based"); @@ -291,8 +300,7 @@ public void testMqtt5Features() { } public void testMqtt3Features() { - final Mqtt3FeatureTester mqtt3Tester = new Mqtt3FeatureTester( - Objects.requireNonNull(host), + final Mqtt3FeatureTester mqtt3Tester = new Mqtt3FeatureTester(Objects.requireNonNull(host), Objects.requireNonNull(port), authenticationOptions.getUser(), authenticationOptions.getPassword(), @@ -401,10 +409,32 @@ public void testMqtt3Features() { @Override public @NotNull String toString() { - return "TestBrokerCommand{" + "host='" + host + '\'' + ", port=" + port + ", version=" + version + - ", testAll=" + testAll + ", timeOut=" + timeOut + ", qosTries=" + qosTries + ", logToLogfile=" + - logToLogfile + ", authenticationOptions=" + authenticationOptions + ", sslOptions=" + sslOptions + - ", defaultOptions=" + defaultOptions + ", defaultCLIProperties=" + defaultCLIProperties + - ", sslConfig=" + sslConfig + '}'; + return "TestBrokerCommand{" + + "host='" + + host + + '\'' + + ", port=" + + port + + ", version=" + + version + + ", testAll=" + + testAll + + ", timeOut=" + + timeOut + + ", qosTries=" + + qosTries + + ", logToLogfile=" + + logToLogfile + + ", authenticationOptions=" + + authenticationOptions + + ", sslOptions=" + + sslOptions + + ", defaultOptions=" + + defaultOptions + + ", defaultCLIProperties=" + + defaultCLIProperties + + ", sslConfig=" + + sslConfig + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java index 05ef46412..92dd8ed16 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/HiveMQCLICommand.java @@ -23,10 +23,14 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "hivemq", description = "HiveMQ Command Line Interpreter.", - synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, - mixinStandardHelpOptions = true) +@CommandLine.Command(name = "hivemq", + description = "HiveMQ Command Line Interpreter.", + synopsisHeading = "%n@|bold Usage:|@ ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class HiveMQCLICommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @@ -34,7 +38,8 @@ public class HiveMQCLICommand implements Callable { private @NotNull CommandLine.Model.CommandSpec spec; @Inject - public HiveMQCLICommand() {} + public HiveMQCLICommand() { + } @Override public @NotNull Integer call() { diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java index 6d087cbb8..8a83529cb 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/ExportCommand.java @@ -23,10 +23,14 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "export", description = "Exports the specified details from HiveMQ", - synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, - mixinStandardHelpOptions = true) +@CommandLine.Command(name = "export", + description = "Exports the specified details from HiveMQ", + synopsisHeading = "%n@|bold Usage:|@ ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class ExportCommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @@ -34,7 +38,8 @@ public class ExportCommand implements Callable { private @NotNull CommandLine.Model.CommandSpec spec; @Inject - public ExportCommand() {} + public ExportCommand() { + } @Override public @NotNull Integer call() { diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsCsvWriterTask.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsCsvWriterTask.java index 12feacad2..7f466d42c 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsCsvWriterTask.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsCsvWriterTask.java @@ -16,7 +16,13 @@ package com.hivemq.cli.commands.hivemq.export.clients; -import com.hivemq.cli.openapi.hivemq.*; +import com.hivemq.cli.openapi.hivemq.CertificateInformation; +import com.hivemq.cli.openapi.hivemq.ClientDetails; +import com.hivemq.cli.openapi.hivemq.ClientRestrictions; +import com.hivemq.cli.openapi.hivemq.ConnectionDetails; +import com.hivemq.cli.openapi.hivemq.ProxyInformation; +import com.hivemq.cli.openapi.hivemq.TLV; +import com.hivemq.cli.openapi.hivemq.TlsInformation; import com.opencsv.CSVWriter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,13 +44,38 @@ public class ClientDetailsCsvWriterTask implements Runnable { static final @NotNull String @NotNull [] EXPORT_CSV_HEADER = { - "clientId", "connected", "sessionExpiryInterval", "connectedAt", "messageQueueSize", "willPresent", - "maxMessageSize", "maxQueueSize", "queuedMessageStrategy", "ip", "sourceIp", "sourcePort", "destinationIp", - "destinationPort", "tlvs", "mqttVersion", "connectedListenerId", "connectedNodeId", "keepAlive", "username", - "password", "cleanStart", "cipherSuite", "tlsVersion", "certificateCommonName", "certificateOrganization", - "certificateOrganizationalUnit", "certificateSerial", "certificateValidFrom", "certificateValidUntil", - "certificateCountry", "certificateState" - }; + "clientId", + "connected", + "sessionExpiryInterval", + "connectedAt", + "messageQueueSize", + "willPresent", + "maxMessageSize", + "maxQueueSize", + "queuedMessageStrategy", + "ip", + "sourceIp", + "sourcePort", + "destinationIp", + "destinationPort", + "tlvs", + "mqttVersion", + "connectedListenerId", + "connectedNodeId", + "keepAlive", + "username", + "password", + "cleanStart", + "cipherSuite", + "tlsVersion", + "certificateCommonName", + "certificateOrganization", + "certificateOrganizationalUnit", + "certificateSerial", + "certificateValidFrom", + "certificateValidUntil", + "certificateCountry", + "certificateState"}; private final @NotNull AtomicLong writtenClientDetails = new AtomicLong(0); private final @NotNull CompletableFuture clientDetailsFuture; @@ -100,7 +131,9 @@ public void run() { Logger.debug("Finished writing {} client details to CSV file {}", writtenClientDetails, file.getAbsolutePath()); } - public long getWrittenClientDetails() {return writtenClientDetails.get();} + public long getWrittenClientDetails() { + return writtenClientDetails.get(); + } private void writeHeader() { csvWriter.writeNext(EXPORT_CSV_HEADER); diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsRetrieverTask.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsRetrieverTask.java index 4321937af..6d90c782e 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsRetrieverTask.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ClientDetailsRetrieverTask.java @@ -26,7 +26,11 @@ import java.util.List; import java.util.Map; -import java.util.concurrent.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class ClientDetailsRetrieverTask implements Runnable { diff --git a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java index e14840c46..cac024b39 100644 --- a/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/hivemq/export/clients/ExportClientsCommand.java @@ -17,7 +17,11 @@ package com.hivemq.cli.commands.hivemq.export.clients; import com.google.common.base.Throwables; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; import com.hivemq.cli.MqttCLIMain; import com.hivemq.cli.openapi.ApiException; import com.hivemq.cli.openapi.hivemq.ClientDetails; @@ -36,11 +40,21 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Objects; -import java.util.concurrent.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; -@CommandLine.Command(name = "clients", description = "Export HiveMQ client details", sortOptions = false, - versionProvider = MqttCLIMain.CLIVersionProvider.class, mixinStandardHelpOptions = true) +@CommandLine.Command(name = "clients", + description = "Export HiveMQ client details", + sortOptions = false, + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class ExportClientsCommand implements Callable { public enum OutputFormat { @@ -48,52 +62,68 @@ public enum OutputFormat { } @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-url"}, defaultValue = "http://localhost:8888", - description = "The URL of the HiveMQ REST API endpoint (default http://localhost:8888)", order = 1) + @CommandLine.Option(names = {"-url"}, + defaultValue = "http://localhost:8888", + description = "The URL of the HiveMQ REST API endpoint (default http://localhost:8888)", + order = 1) private @NotNull String url; @CommandLine.Option(names = {"-f", "--file"}, - description = "The file to write the output to (defaults to a timestamped file in the current working directory)", - order = 2) + description = "The file to write the output to (defaults to a timestamped file in the current working directory)", + order = 2) private @Nullable File file; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--rate"}, defaultValue = "1500", - description = "The rate limit of the rest calls to the HiveMQ API endpoint in requests per second (default 1500 rps)", - order = 3) + @CommandLine.Option(names = {"-r", "--rate"}, + defaultValue = "1500", + description = "The rate limit of the rest calls to the HiveMQ API endpoint in requests per second (default 1500 rps)", + order = 3) private double rateLimit; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"--format"}, defaultValue = "csv", - description = "The export output format (default csv)", order = 4) + @CommandLine.Option(names = {"--format"}, + defaultValue = "csv", + description = "The export output format (default csv)", + order = 4) private @NotNull OutputFormat format; @SuppressWarnings("unused") - @CommandLine.Option(names = {"--csvSeparator"}, defaultValue = "" + CSVWriter.DEFAULT_SEPARATOR, - description = "The separator for CSV export (default " + CSVWriter.DEFAULT_SEPARATOR + ")", order = 5) + @CommandLine.Option(names = {"--csvSeparator"}, + defaultValue = "" + CSVWriter.DEFAULT_SEPARATOR, + description = "The separator for CSV export (default " + CSVWriter.DEFAULT_SEPARATOR + ")", + order = 5) private char csvSeparator; @SuppressWarnings("unused") - @CommandLine.Option(names = {"--csvQuoteChar"}, defaultValue = "" + CSVWriter.DEFAULT_QUOTE_CHARACTER, - description = "The quote character for csv export (default " + CSVWriter.DEFAULT_QUOTE_CHARACTER + ")", - order = 6) + @CommandLine.Option(names = {"--csvQuoteChar"}, + defaultValue = "" + CSVWriter.DEFAULT_QUOTE_CHARACTER, + description = "The quote character for csv export (default " + + CSVWriter.DEFAULT_QUOTE_CHARACTER + + ")", + order = 6) private char csvQuoteCharacter; @SuppressWarnings("unused") - @CommandLine.Option(names = {"--csvEscChar"}, defaultValue = "" + CSVWriter.DEFAULT_ESCAPE_CHARACTER, - description = "The escape character for csv export (default " + CSVWriter.DEFAULT_ESCAPE_CHARACTER + ")", - order = 7) + @CommandLine.Option(names = {"--csvEscChar"}, + defaultValue = "" + CSVWriter.DEFAULT_ESCAPE_CHARACTER, + description = "The escape character for csv export (default " + + CSVWriter.DEFAULT_ESCAPE_CHARACTER + + ")", + order = 7) private char csvEscapeChar; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"--csvLineEndChar"}, defaultValue = CSVWriter.DEFAULT_LINE_END, - description = "The line-end character for csv export (default \\n)", order = 8) + @CommandLine.Option(names = {"--csvLineEndChar"}, + defaultValue = CSVWriter.DEFAULT_LINE_END, + description = "The line-end character for csv export (default \\n)", + order = 8) private @NotNull String csvLineEndCharacter; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", - order = 9) + @CommandLine.Option(names = {"-l"}, + defaultValue = "false", + description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", + order = 9) private void initLogging(final boolean logToLogfile) { LoggerUtils.turnOffConsoleLogging(logToLogfile); } @@ -173,11 +203,8 @@ public ExportClientsCommand() { // Start printing final ScheduledExecutorService printingScheduler = Executors.newScheduledThreadPool(1); printingScheduler.scheduleWithFixedDelay(new PrintingTask(clientIdsRetrieverTask, - clientIdsRetrieverFuture, - clientDetailsCsvWriterTask), - 100, - 500, - TimeUnit.MILLISECONDS); + clientIdsRetrieverFuture, + clientDetailsCsvWriterTask), 100, 500, TimeUnit.MILLISECONDS); // Handle completion of all futures @@ -197,9 +224,26 @@ public ExportClientsCommand() { @Override public @NotNull String toString() { - return "ExportClientsCommand{" + "url='" + url + '\'' + ", file=" + file + ", rateLimit=" + rateLimit + - ", format=" + format + ", csvSeparator=" + csvSeparator + ", csvQuoteCharacter=" + csvQuoteCharacter + - ", csvEscapeChar=" + csvEscapeChar + ", csvLineEndCharacter='" + csvLineEndCharacter + '\'' + '}'; + return "ExportClientsCommand{" + + "url='" + + url + + '\'' + + ", file=" + + file + + ", rateLimit=" + + rateLimit + + ", format=" + + format + + ", csvSeparator=" + + csvSeparator + + ", csvQuoteCharacter=" + + csvQuoteCharacter + + ", csvEscapeChar=" + + csvEscapeChar + + ", csvLineEndCharacter='" + + csvLineEndCharacter + + '\'' + + '}'; } private static class PrintingTask implements Runnable { @@ -271,22 +315,25 @@ public ExportCompletedHandler( Throwables.getRootCause(throwable).getMessage()); } } else { - System.err.println( - "\rFailed to retrieve client details: " + Throwables.getRootCause(throwable).getMessage()); + System.err.println("\rFailed to retrieve client details: " + + Throwables.getRootCause(throwable).getMessage()); } if (clientDetailsCsvWriterTask.getWrittenClientDetails() > 0) { - System.out.println( - "Wrote " + clientDetailsCsvWriterTask.getWrittenClientDetails() + " client details to " + - Objects.requireNonNull(file).getPath()); + System.out.println("Wrote " + + clientDetailsCsvWriterTask.getWrittenClientDetails() + + " client details to " + + Objects.requireNonNull(file).getPath()); } else { Objects.requireNonNull(file).delete(); } return -1; // Export failed } else { - System.out.println("\rSuccessfully exported " + clientDetailsCsvWriterTask.getWrittenClientDetails() + - " client details to " + Objects.requireNonNull(file).getPath()); + System.out.println("\rSuccessfully exported " + + clientDetailsCsvWriterTask.getWrittenClientDetails() + + " client details to " + + Objects.requireNonNull(file).getPath()); return 0; // Export was successful } } diff --git a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java index f84023c6e..d9c4c77b1 100644 --- a/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/AuthenticationOptions.java @@ -34,21 +34,27 @@ public class AuthenticationOptions { @CommandLine.Option(names = {"-u", "--user"}, description = "The username for authentication") private @Nullable String user; - @CommandLine.Option(names = {"-pw", "--password"}, arity = "0..1", interactive = true, - converter = ByteBufferConverter.class, description = "The password for authentication") + @CommandLine.Option(names = {"-pw", "--password"}, + arity = "0..1", + interactive = true, + converter = ByteBufferConverter.class, + description = "The password for authentication") private @Nullable ByteBuffer password; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-pw:env"}, arity = "0..1", converter = EnvVarToByteBufferConverter.class, - fallbackValue = "MQTT_CLI_PW", - description = "The password for authentication read in from an environment variable") + @CommandLine.Option(names = {"-pw:env"}, + arity = "0..1", + converter = EnvVarToByteBufferConverter.class, + fallbackValue = "MQTT_CLI_PW", + description = "The password for authentication read in from an environment variable") private void setPasswordFromEnv(final @NotNull ByteBuffer passwordEnvironmentVariable) { password = passwordEnvironmentVariable; } @SuppressWarnings("unused") - @CommandLine.Option(names = {"-pw:file"}, converter = PasswordFileToByteBufferConverter.class, - description = "The password for authentication read in from a file") + @CommandLine.Option(names = {"-pw:file"}, + converter = PasswordFileToByteBufferConverter.class, + description = "The password for authentication read in from a file") private void setPasswordFromFile(final @NotNull ByteBuffer passwordFromFile) { password = passwordFromFile; } diff --git a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java index c606311a0..0ec922224 100644 --- a/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/ConnectOptions.java @@ -41,43 +41,49 @@ public class ConnectOptions { - @CommandLine.Option(names = {"-V", "--mqttVersion"}, converter = MqttVersionConverter.class, - description = "The MQTT version used by the client (default: 5)") + @CommandLine.Option(names = {"-V", "--mqttVersion"}, + converter = MqttVersionConverter.class, + description = "The MQTT version used by the client (default: 5)") private @Nullable MqttVersion version; @CommandLine.Option(names = {"-h", "--host"}, - description = "The hostname of the message broker (default 'localhost')") + description = "The hostname of the message broker (default 'localhost')") private @Nullable String host; @CommandLine.Option(names = {"-p", "--port"}, description = "The port of the message broker (default: 1883)") private @Nullable Integer port; @CommandLine.Option(names = {"-i", "--identifier"}, - description = "The client identifier UTF-8 String (default randomly generated string)") + description = "The client identifier UTF-8 String (default randomly generated string)") private @Nullable String identifier; @CommandLine.Option(names = {"-ip", "--identifierPrefix"}, - description = "The prefix of the client Identifier UTF-8 String") + description = "The prefix of the client Identifier UTF-8 String") private @Nullable String identifierPrefix; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-k", "--keepAlive"}, converter = UnsignedShortConverter.class, - description = "A keep alive of the client (in seconds) (default: 60)") + @CommandLine.Option(names = {"-k", "--keepAlive"}, + converter = UnsignedShortConverter.class, + description = "A keep alive of the client (in seconds) (default: 60)") private @Nullable Integer keepAlive; @SuppressWarnings("unused") - @CommandLine.Option(names = {"--no-cleanStart"}, negatable = true, defaultValue = "true", + @CommandLine.Option(names = {"--no-cleanStart"}, + negatable = true, + defaultValue = "true", description = "Define a clean start for the connection (default: true)") private boolean cleanStart; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-se", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The lifetime of the session of the connected client") + @CommandLine.Option(names = {"-se", "--sessionExpiryInterval"}, + converter = UnsignedIntConverter.class, + description = "The lifetime of the session of the connected client") private @Nullable Long sessionExpiryInterval; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-Cup", "--connectUserProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the connect message'") + @CommandLine.Option(names = {"-Cup", "--connectUserProperty"}, + converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the connect message'") private @Nullable Mqtt5UserProperty @Nullable [] connectUserProperties; @SuppressWarnings("unused") @@ -241,12 +247,41 @@ public void logUnusedOptions() { @Override public @NotNull String toString() { - return "ConnectOptions{" + "version=" + version + ", host='" + host + '\'' + ", port=" + port + - ", identifier='" + identifier + '\'' + ", identifierPrefix='" + identifierPrefix + '\'' + - ", keepAlive=" + keepAlive + ", cleanStart=" + cleanStart + ", sessionExpiryInterval=" + - sessionExpiryInterval + ", connectUserProperties=" + Arrays.toString(connectUserProperties) + - ", useWebSocket=" + useWebSocket + ", webSocketPath='" + webSocketPath + '\'' + ", willOptions=" + - willOptions + ", connectRestrictionOptions=" + connectRestrictionOptions + ", authenticationOptions=" + - authenticationOptions + ", sslOptions=" + sslOptions + '}'; + return "ConnectOptions{" + + "version=" + + version + + ", host='" + + host + + '\'' + + ", port=" + + port + + ", identifier='" + + identifier + + '\'' + + ", identifierPrefix='" + + identifierPrefix + + '\'' + + ", keepAlive=" + + keepAlive + + ", cleanStart=" + + cleanStart + + ", sessionExpiryInterval=" + + sessionExpiryInterval + + ", connectUserProperties=" + + Arrays.toString(connectUserProperties) + + ", useWebSocket=" + + useWebSocket + + ", webSocketPath='" + + webSocketPath + + '\'' + + ", willOptions=" + + willOptions + + ", connectRestrictionOptions=" + + connectRestrictionOptions + + ", authenticationOptions=" + + authenticationOptions + + ", sslOptions=" + + sslOptions + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java index 98e051d9e..ba5198dff 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DefaultOptions.java @@ -35,7 +35,11 @@ public class DefaultOptions { @Override public @NotNull String toString() { - return "DefaultOptions{" + "versionInfoRequested=" + versionInfoRequested + ", usageHelpRequested=" + - usageHelpRequested + '}'; + return "DefaultOptions{" + + "versionInfoRequested=" + + versionInfoRequested + + ", usageHelpRequested=" + + usageHelpRequested + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java index 2ba817b4d..c84a5f58a 100644 --- a/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/DisconnectOptions.java @@ -32,22 +32,24 @@ public class DisconnectOptions { @CommandLine.Option(names = {"-h", "--host"}, - description = "The hostname of the message broker (default 'localhost')") + description = "The hostname of the message broker (default 'localhost')") private @Nullable String host; @SuppressWarnings("unused") @CommandLine.Option(names = {"-i", "--identifier"}, - description = "The client identifier UTF-8 String (default randomly generated string)") + description = "The client identifier UTF-8 String (default randomly generated string)") private @Nullable String clientIdentifier; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-a", "--all"}, defaultValue = "false", - description = "Disconnect all connected clients") + @CommandLine.Option(names = {"-a", "--all"}, + defaultValue = "false", + description = "Disconnect all connected clients") private boolean disconnectAll; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-e", "--sessionExpiryInterval"}, converter = UnsignedIntConverter.class, - description = "The session expiry of the disconnect (default: 0)") + @CommandLine.Option(names = {"-e", "--sessionExpiryInterval"}, + converter = UnsignedIntConverter.class, + description = "The session expiry of the disconnect (default: 0)") private @Nullable Long sessionExpiryInterval; @SuppressWarnings("unused") @@ -55,8 +57,9 @@ public class DisconnectOptions { private @Nullable String reasonString; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the disconnect message") + @CommandLine.Option(names = {"-up", "--userProperty"}, + converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the disconnect message") private @Nullable Mqtt5UserProperty @Nullable [] userProperties; public boolean isDisconnectAll() { @@ -105,8 +108,22 @@ public void logUnusedDisconnectOptions(final @NotNull MqttVersion mqttVersion) { @Override public @NotNull String toString() { - return "DisconnectOptions{" + "host='" + host + '\'' + ", identifier='" + clientIdentifier + '\'' + - ", disconnectAll=" + disconnectAll + ", sessionExpiryInterval=" + sessionExpiryInterval + - ", reasonString='" + reasonString + '\'' + ", userProperties=" + Arrays.toString(userProperties) + '}'; + return "DisconnectOptions{" + + "host='" + + host + + '\'' + + ", identifier='" + + clientIdentifier + + '\'' + + ", disconnectAll=" + + disconnectAll + + ", sessionExpiryInterval=" + + sessionExpiryInterval + + ", reasonString='" + + reasonString + + '\'' + + ", userProperties=" + + Arrays.toString(userProperties) + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java index 1f39496be..7105c3ccd 100644 --- a/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/MessagePayloadOptions.java @@ -26,14 +26,17 @@ public class MessagePayloadOptions { @SuppressWarnings("unused") - @CommandLine.Option(names = {"-m", "--message"}, converter = ByteBufferConverter.class, - description = "The message to publish") + @CommandLine.Option(names = {"-m", "--message"}, + converter = ByteBufferConverter.class, + description = "The message to publish") private void setMessageFromCommandline(final @NotNull ByteBuffer messageFromFile) { messageBuffer = messageFromFile; } @SuppressWarnings("unused") - @CommandLine.Option(names = {"-m:empty", "--message-empty"}, defaultValue = "false", description = "Sets the message to an empty payload") + @CommandLine.Option(names = {"-m:empty", "--message-empty"}, + defaultValue = "false", + description = "Sets the message to an empty payload") private void setMessageToEmpty(final boolean isEmpty) { if (isEmpty) { messageBuffer = ByteBuffer.allocate(0); @@ -41,8 +44,9 @@ private void setMessageToEmpty(final boolean isEmpty) { } @SuppressWarnings("unused") - @CommandLine.Option(names = {"-m:file", "--message-file"}, converter = FileToByteBufferConverter.class, - description = "The message read in from a file") + @CommandLine.Option(names = {"-m:file", "--message-file"}, + converter = FileToByteBufferConverter.class, + description = "The message read in from a file") private void setMessageFromFile(final @NotNull ByteBuffer messageFromFile) { messageBuffer = messageFromFile; } diff --git a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java index 346640955..dc0eb79bc 100644 --- a/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/PublishOptions.java @@ -71,9 +71,7 @@ public class PublishOptions { private @Nullable Mqtt5PayloadFormatIndicator payloadFormatIndicator; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-ct", "--contentType"}, - description = "A description of publish message's content", - order = 1) + @CommandLine.Option(names = {"-ct", "--contentType"}, description = "A description of publish message's content") private @Nullable String contentType; @SuppressWarnings("unused") diff --git a/src/main/java/com/hivemq/cli/commands/options/SslOptions.java b/src/main/java/com/hivemq/cli/commands/options/SslOptions.java index df2535741..3dff0191c 100644 --- a/src/main/java/com/hivemq/cli/commands/options/SslOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/SslOptions.java @@ -31,7 +31,11 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; import java.io.IOException; -import java.security.*; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -43,41 +47,53 @@ public class SslOptions { private static final @NotNull String DEFAULT_TLS_VERSION = "TLSv1.2"; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-s", "--secure"}, defaultValue = "false", - description = "Use default ssl configuration if no other ssl options are specified (default: false)") + @CommandLine.Option(names = {"-s", "--secure"}, + defaultValue = "false", + description = "Use default ssl configuration if no other ssl options are specified (default: false)") private boolean useSsl; - @CommandLine.Option(names = {"--cafile"}, paramLabel = "FILE", converter = FileToCertificatesConverter.class, - description = "Path to a file containing trusted CA certificates to enable encrypted certificate based communication") + @CommandLine.Option(names = {"--cafile"}, + paramLabel = "FILE", + converter = FileToCertificatesConverter.class, + description = "Path to a file containing trusted CA certificates to enable encrypted certificate based communication") private @Nullable Collection serverCertificateChain; @SuppressWarnings("unused") - @CommandLine.Option(names = {"--capath"}, paramLabel = "DIR", converter = DirectoryToCertificatesConverter.class, - description = { - "Path to a directory containing certificate files to import to enable encrypted certificate based communication" - }) + @CommandLine.Option(names = {"--capath"}, + paramLabel = "DIR", + converter = DirectoryToCertificatesConverter.class, + description = { + "Path to a directory containing certificate files to import to enable encrypted certificate based communication"}) private @Nullable Collection serverCertificateChainFromDir; @SuppressWarnings("unused") - @CommandLine.Option(names = {"--ciphers"}, split = ":", - description = "The client supported cipher suites list in IANA format separated with ':'") + @CommandLine.Option(names = {"--ciphers"}, + split = ":", + description = "The client supported cipher suites list in IANA format separated with ':'") private @Nullable Collection cipherSuites; @CommandLine.Option(names = {"--tls-version"}, - description = "The TLS protocol version to use (default: {'TLSv.1.2'})") + description = "The TLS protocol version to use (default: {'TLSv.1.2'})") private @Nullable Collection supportedTLSVersions; - @CommandLine.Option(names = {"--cert"}, converter = FileToCertificatesConverter.class, - description = "The client certificate to use for client side authentication") + @CommandLine.Option(names = {"--cert"}, + converter = FileToCertificatesConverter.class, + description = "The client certificate to use for client side authentication") private @Nullable Collection clientCertificateChain; - @CommandLine.Option(names = {"--key"}, converter = FileToPrivateKeyConverter.class, - description = "The path to the client private key for client side authentication") + @CommandLine.Option(names = {"--key"}, + converter = FileToPrivateKeyConverter.class, + description = "The path to the client private key for client side authentication") private @Nullable PrivateKey clientPrivateKey; private boolean useBuiltSslConfig() { - return serverCertificateChain != null || serverCertificateChainFromDir != null || cipherSuites != null || - supportedTLSVersions != null || clientPrivateKey != null || clientCertificateChain != null || useSsl; + return serverCertificateChain != null || + serverCertificateChainFromDir != null || + cipherSuites != null || + supportedTLSVersions != null || + clientPrivateKey != null || + clientCertificateChain != null || + useSsl; } public @Nullable MqttClientSslConfig buildSslConfig() throws Exception { @@ -205,9 +221,21 @@ private void setDefaultOptions() { @Override public @NotNull String toString() { - return "SslOptions{" + "useSsl=" + useSsl + ", serverCertificates=" + serverCertificateChain + - ", serverCertificatesFromDir=" + serverCertificateChainFromDir + ", cipherSuites=" + cipherSuites + - ", supportedTLSVersions=" + supportedTLSVersions + ", clientCertificates=" + clientCertificateChain + - ", clientPrivateKey=" + clientPrivateKey + '}'; + return "SslOptions{" + + "useSsl=" + + useSsl + + ", serverCertificates=" + + serverCertificateChain + + ", serverCertificatesFromDir=" + + serverCertificateChainFromDir + + ", cipherSuites=" + + cipherSuites + + ", supportedTLSVersions=" + + supportedTLSVersions + + ", clientCertificates=" + + clientCertificateChain + + ", clientPrivateKey=" + + clientPrivateKey + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java index a88643f37..afe88264f 100644 --- a/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/SubscribeOptions.java @@ -42,40 +42,42 @@ public class SubscribeOptions { private @NotNull String @NotNull [] topics; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) //will be initialized via default value - @CommandLine.Option(names = {"-q", "--qos"}, converter = MqttQosConverter.class, defaultValue = "2", - description = "Quality of service for the corresponding topics (default for all: 2)") + @CommandLine.Option(names = {"-q", "--qos"}, + converter = MqttQosConverter.class, + defaultValue = "2", + description = "Quality of service for the corresponding topics (default for all: 2)") private @NotNull MqttQos @NotNull [] qos; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property of the subscribe message") + @CommandLine.Option(names = {"-up", "--userProperty"}, + converter = Mqtt5UserPropertyConverter.class, + description = "A user property of the subscribe message") private @Nullable Mqtt5UserProperty @Nullable [] userProperties; @SuppressWarnings("unused") @CommandLine.Option(names = {"-of", "--outputToFile"}, - description = "A file to which the received publish messages will be written") + description = "A file to which the received publish messages will be written") private @Nullable File outputFile; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-oc", "--outputToConsole"}, hidden = true, defaultValue = "true", - description = "The received messages will be written to the console (default: true)") - private boolean printToSTDOUT; - @SuppressWarnings("unused") @CommandLine.Option(names = {"-b64", "--base64"}, - description = "Specify the encoding of the received messages as Base64 (default: false)") + description = "Specify the encoding of the received messages as Base64 (default: false)") private boolean base64; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-J", "--jsonOutput"}, defaultValue = "false", - description = "Print the received publishes in pretty JSON format") + @CommandLine.Option(names = {"-J", "--jsonOutput"}, + defaultValue = "false", + description = "Print the received publishes in pretty JSON format") private boolean jsonOutput; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-T", "--showTopics"}, defaultValue = "false", - description = "Prepend the specific topic name to the received publish") + @CommandLine.Option(names = {"-T", "--showTopics"}, + defaultValue = "false", + description = "Prepend the specific topic name to the received publish") private boolean showTopics; + private boolean printToSTDOUT = false; + public SubscribeOptions() { setDefaultOptions(); } @@ -96,9 +98,13 @@ public boolean isPrintToSTDOUT() { return printToSTDOUT; } - public boolean isBase64() {return base64;} + public boolean isBase64() { + return base64; + } - public boolean isJsonOutput() {return jsonOutput;} + public boolean isJsonOutput() { + return jsonOutput; + } public @Nullable Mqtt5UserProperties getUserProperties() { return MqttUtils.convertToMqtt5UserProperties(userProperties); @@ -163,9 +169,23 @@ public void setDefaultOptions() { @Override public @NotNull String toString() { - return "SubscribeOptions{" + "topics=" + Arrays.toString(topics) + ", qos=" + Arrays.toString(qos) + - ", userProperties=" + Arrays.toString(userProperties) + ", outputFile=" + outputFile + - ", printToSTDOUT=" + printToSTDOUT + ", base64=" + base64 + ", jsonOutput=" + jsonOutput + - ", showTopics=" + showTopics + '}'; + return "SubscribeOptions{" + + "topics=" + + Arrays.toString(topics) + + ", qos=" + + Arrays.toString(qos) + + ", userProperties=" + + Arrays.toString(userProperties) + + ", outputFile=" + + outputFile + + ", printToSTDOUT=" + + printToSTDOUT + + ", base64=" + + base64 + + ", jsonOutput=" + + jsonOutput + + ", showTopics=" + + showTopics + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java index 51b2b9021..9895003df 100644 --- a/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java +++ b/src/main/java/com/hivemq/cli/commands/options/UnsubscribeOptions.java @@ -37,8 +37,9 @@ public class UnsubscribeOptions { private @NotNull String @NotNull [] topics; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-up", "--userProperty"}, converter = Mqtt5UserPropertyConverter.class, - description = "A user property for the unsubscribe message") + @CommandLine.Option(names = {"-up", "--userProperty"}, + converter = Mqtt5UserPropertyConverter.class, + description = "A user property for the unsubscribe message") private @Nullable Mqtt5UserProperty @Nullable [] userProperties; public UnsubscribeOptions() { @@ -71,8 +72,7 @@ public UnsubscribeOptions( public void logUnusedUnsubscribeOptions(final @NotNull MqttVersion mqttVersion) { if (mqttVersion == MqttVersion.MQTT_3_1_1) { if (userProperties != null) { - Logger.warn( - "Unsubscribe user properties were set but are unused in MQTT Version {}", + Logger.warn("Unsubscribe user properties were set but are unused in MQTT Version {}", MqttVersion.MQTT_3_1_1); } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java index 6a33e813d..844c1bd99 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ClearScreenCommand.java @@ -29,7 +29,8 @@ public class ClearScreenCommand implements Callable { @Inject - ClearScreenCommand() {} + ClearScreenCommand() { + } @Override public @NotNull Integer call() { @@ -41,4 +42,4 @@ public class ClearScreenCommand implements Callable { public @NotNull String toString() { return "ClearScreenCommand{}"; } -} \ No newline at end of file +} diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java index dbf948c0c..8f0719c88 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextDisconnectCommand.java @@ -70,7 +70,11 @@ public ContextDisconnectCommand(final @NotNull MqttClientExecutor executor) { @Override public @NotNull String toString() { - return "ContextDisconnectCommand{" + "disconnectOptions=" + disconnectOptions + ", defaultOptions=" + - defaultOptions + '}'; + return "ContextDisconnectCommand{" + + "disconnectOptions=" + + disconnectOptions + + ", defaultOptions=" + + defaultOptions + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java index 1b6aca399..f3c91c6d3 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextPublishCommand.java @@ -26,8 +26,10 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "pub", aliases = "publish", description = "Publish a message to a list of topics", - mixinStandardHelpOptions = true) +@CommandLine.Command(name = "pub", + aliases = "publish", + description = "Publish a message to a list of topics", + mixinStandardHelpOptions = true) public class ContextPublishCommand extends ShellContextCommand implements Callable { @CommandLine.Mixin diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java index 763f1ae8d..41d15df2a 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSubscribeCommand.java @@ -32,17 +32,28 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -@CommandLine.Command(name = "sub", aliases = "subscribe", - description = "Subscribe this MQTT client to a list of topics", mixinStandardHelpOptions = true) +@CommandLine.Command(name = "sub", + aliases = "subscribe", + description = "Subscribe this MQTT client to a list of topics", + mixinStandardHelpOptions = true) public class ContextSubscribeCommand extends ShellContextCommand implements Callable { private static final int IDLE_TIME = 1000; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-s", "--stay"}, defaultValue = "false", - description = "The subscribe will block the console and wait for publish messages to print (default: false)") + @CommandLine.Option(names = {"-s", "--stay"}, + defaultValue = "false", + description = "The subscribe will block the console and wait for publish messages to print (default: false)") private boolean stay; + @SuppressWarnings("unused") + @CommandLine.Option(names = {"-oc", "--outputToConsole"}, + defaultValue = "false", + description = "The received messages will be written to the console (default: true)") + private void printToSTDOUT(final boolean printToSTDOUT) { + subscribeOptions.setPrintToSTDOUT(printToSTDOUT); + } + @CommandLine.Mixin private final @NotNull SubscribeOptions subscribeOptions = new SubscribeOptions(); @@ -64,8 +75,8 @@ public ContextSubscribeCommand(final @NotNull MqttClientExecutor mqttClientExecu subscribeOptions.logUnusedOptions(contextClient.getConfig().getMqttVersion()); subscribeOptions.arrangeQosToMatchTopics(); - if (!stay) { - subscribeOptions.setPrintToSTDOUT(false); + if (stay) { + subscribeOptions.setPrintToSTDOUT(true); } if (subscribeOptions.isOutputFileInvalid(subscribeOptions.getOutputFile())) { diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java index c647d83c2..81d3b2eaf 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextSwitchCommand.java @@ -37,11 +37,12 @@ public class ContextSwitchCommand extends ShellContextCommand implements Callabl private @Nullable String contextName; @CommandLine.Option(names = {"-i", "--identifier"}, - description = "The client identifier UTF-8 String (default randomly generated string)") + description = "The client identifier UTF-8 String (default randomly generated string)") private @Nullable String identifier; - @CommandLine.Option(names = {"-h", "--host"}, defaultValue = "localhost", - description = "The hostname of the message broker (default 'localhost')") + @CommandLine.Option(names = {"-h", "--host"}, + defaultValue = "localhost", + description = "The hostname of the message broker (default 'localhost')") private @Nullable String host; @Inject @@ -95,7 +96,16 @@ private void extractKeyFromContextName(final String contextName) { @Override public @NotNull String toString() { - return "ContextSwitchCommand{" + "contextName='" + contextName + '\'' + ", identifier='" + identifier + '\'' + - ", host='" + host + '\'' + '}'; + return "ContextSwitchCommand{" + + "contextName='" + + contextName + + '\'' + + ", identifier='" + + identifier + + '\'' + + ", host='" + + host + + '\'' + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java index 35eb81815..33d6eec21 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ContextUnsubscribeCommand.java @@ -26,8 +26,10 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "unsub", aliases = "unsubscribe", - description = "Unsubscribe this MQTT client from a list of topics", mixinStandardHelpOptions = true) +@CommandLine.Command(name = "unsub", + aliases = "unsubscribe", + description = "Unsubscribe this MQTT client from a list of topics", + mixinStandardHelpOptions = true) public class ContextUnsubscribeCommand extends ShellContextCommand implements Callable { @CommandLine.Mixin diff --git a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java index 9f753351d..6ae46be75 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ListClientsCommand.java @@ -26,12 +26,18 @@ import javax.inject.Inject; import java.io.PrintWriter; import java.time.LocalDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.Callable; import java.util.stream.Collectors; -@CommandLine.Command(name = "ls", aliases = "list", - description = "List all connected clients with their respective identifiers", mixinStandardHelpOptions = true) +@CommandLine.Command(name = "ls", + aliases = "list", + description = "List all connected clients with their respective identifiers", + mixinStandardHelpOptions = true) public class ListClientsCommand implements Callable { @SuppressWarnings("unused") @@ -43,8 +49,9 @@ public class ListClientsCommand implements Callable { private boolean doNotSort; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-r", "--reverse"}, defaultValue = "false", - description = "reverse order while sorting") + @CommandLine.Option(names = {"-r", "--reverse"}, + defaultValue = "false", + description = "reverse order while sorting") private boolean reverse; @SuppressWarnings("unused") @@ -52,8 +59,9 @@ public class ListClientsCommand implements Callable { private boolean longOutput; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-s", "--subscriptions"}, defaultValue = "false", - description = "list subscribed topics of clients") + @CommandLine.Option(names = {"-s", "--subscriptions"}, + defaultValue = "false", + description = "list subscribed topics of clients") private boolean listSubscriptions; @Inject @@ -102,17 +110,30 @@ public ListClientsCommand() { .max(Integer::compareTo) .orElse("NO_SSL".length()); - final String format = - "%-" + longestState + "s " + "%02d:%02d:%02d " + "%-" + longestID + "s " + "%-" + longestHost + - "s " + "%5d " + "%-" + longestVersion + "s " + "%-" + longestSSLVersion + "s\n"; + final String format = "%-" + + longestState + + "s " + + "%02d:%02d:%02d " + + "%-" + + longestID + + "s " + + "%-" + + longestHost + + "s " + + "%5d " + + "%-" + + longestVersion + + "s " + + "%-" + + longestSSLVersion + + "s\n"; for (final ClientData clientData : sortedClientData) { final MqttClient client = clientData.getClient(); final LocalDateTime dateTime = clientData.getCreationTime(); final String connectionState = client.getState().toString(); - writer.printf( - format, + writer.printf(format, connectionState, dateTime.getHour(), dateTime.getMinute(), @@ -174,7 +195,17 @@ public ListClientsCommand() { @Override public @NotNull String toString() { - return "ListClientsCommand{" + "sortByTime=" + sortByTime + ", doNotSort=" + doNotSort + ", reverse=" + - reverse + ", longOutput=" + longOutput + ", listSubscriptions=" + listSubscriptions + '}'; + return "ListClientsCommand{" + + "sortByTime=" + + sortByTime + + ", doNotSort=" + + doNotSort + + ", reverse=" + + reverse + + ", longOutput=" + + longOutput + + ", listSubscriptions=" + + listSubscriptions + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java index 4b348f383..aa2c54f05 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellCommand.java @@ -43,11 +43,17 @@ import java.util.Objects; import java.util.concurrent.Callable; -@CommandLine.Command(name = "shell", aliases = "sh", versionProvider = MqttCLIMain.CLIVersionProvider.class, - description = "Starts MqttCLI in shell mode, to enable interactive mode with further sub commands.", - footer = {"", "@|bold Press Ctl-C to exit.|@"}, synopsisHeading = "%n@|bold Usage|@: ", - descriptionHeading = "%n", optionListHeading = "%n@|bold Options|@:%n", - commandListHeading = "%n@|bold Commands|@:%n", separator = " ", mixinStandardHelpOptions = true) +@CommandLine.Command(name = "shell", + aliases = "sh", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + description = "Starts MqttCLI in shell mode, to enable interactive mode with further sub commands.", + footer = {"", "@|bold Press Ctl-C to exit.|@"}, + synopsisHeading = "%n@|bold Usage|@: ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options|@:%n", + commandListHeading = "%n@|bold Commands|@:%n", + separator = " ", + mixinStandardHelpOptions = true) public class ShellCommand implements Callable { private static final @NotNull String DEFAULT_PROMPT = "mqtt> "; @@ -64,9 +70,9 @@ public class ShellCommand implements Callable { private static boolean exitShell = false; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)", - order = 1) + @CommandLine.Option(names = {"-l"}, + defaultValue = "false", + description = "Log to $HOME/.mqtt-cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private boolean logToLogfile; @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @@ -114,11 +120,9 @@ public class ShellCommand implements Callable { TERMINAL_WRITER.println(shellCommandLine.getUsageMessage()); TERMINAL_WRITER.flush(); - TERMINAL_WRITER.printf( - "Using default values from properties file %s:\n", + TERMINAL_WRITER.printf("Using default values from properties file %s:\n", Objects.requireNonNull(defaultCLIProperties.getFile()).getPath()); - TERMINAL_WRITER.printf( - "Host: %s, Port: %d, Mqtt-Version %s, Logfile-Debug-Level: %s\n", + TERMINAL_WRITER.printf("Host: %s, Port: %d, Mqtt-Version %s, Logfile-Debug-Level: %s\n", defaultCLIProperties.getHost(), defaultCLIProperties.getPort(), defaultCLIProperties.getMqttVersion(), @@ -198,7 +202,16 @@ static void clearScreen() { @Override public @NotNull String toString() { - return "ShellCommand{" + "logToLogfile=" + logToLogfile + ", spec=" + spec + ", defaultCLIProperties=" + - defaultCLIProperties + ", logfilePath='" + logfilePath + '\'' + '}'; + return "ShellCommand{" + + "logToLogfile=" + + logToLogfile + + ", spec=" + + spec + + ", defaultCLIProperties=" + + defaultCLIProperties + + ", logfilePath='" + + logfilePath + + '\'' + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java index 5f8a19fa0..655c16644 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellConnectCommand.java @@ -28,8 +28,10 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "con", aliases = "connect", description = "Connect an MQTT client", - abbreviateSynopsis = true) +@CommandLine.Command(name = "con", + aliases = "connect", + description = "Connect an MQTT client", + abbreviateSynopsis = true) public class ShellConnectCommand implements Callable { @CommandLine.Mixin @@ -66,7 +68,13 @@ public ShellConnectCommand(final @NotNull MqttClientExecutor mqttClientExecutor) @Override public @NotNull String toString() { - return "ShellConnectCommand{" + "connectOptions=" + connectOptions + ", defaultOptions=" + defaultOptions + - ", mqttClientExecutor=" + mqttClientExecutor + '}'; + return "ShellConnectCommand{" + + "connectOptions=" + + connectOptions + + ", defaultOptions=" + + defaultOptions + + ", mqttClientExecutor=" + + mqttClientExecutor + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java index 5877eaa71..84d7d7010 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellContextCommand.java @@ -26,11 +26,15 @@ import java.util.Objects; import java.util.concurrent.Callable; -@CommandLine.Command(sortOptions = false, name = "> ", - description = "In context mode all MQTT commands relate to the currently active client.", - synopsisHeading = "%n@|bold Usage|@: ", - synopsisSubcommandLabel = "{ pub | sub | unsub | dis | switch | ls | cls | exit }", descriptionHeading = "%n", - optionListHeading = "%n@|bold Options|@:%n", commandListHeading = "%n@|bold Commands|@:%n", separator = " ") +@CommandLine.Command(sortOptions = false, + name = "> ", + description = "In context mode all MQTT commands relate to the currently active client.", + synopsisHeading = "%n@|bold Usage|@: ", + synopsisSubcommandLabel = "{ pub | sub | unsub | dis | switch | ls | cls | exit }", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options|@:%n", + commandListHeading = "%n@|bold Commands|@:%n", + separator = " ") public class ShellContextCommand implements Callable { public static @Nullable MqttClient contextClient; @@ -64,4 +68,4 @@ public static void removeContext() { public @NotNull String toString() { return "ShellContextCommand{}"; } -} \ No newline at end of file +} diff --git a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java index 16fdf23eb..5ea3cd329 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/ShellDisconnectCommand.java @@ -77,8 +77,15 @@ public class ShellDisconnectCommand implements Callable { @Override public @NotNull String toString() { - return "ShellDisconnectCommand{" + "disconnectOptions=" + disconnectOptions + ", defaultOptions=" + - defaultOptions + ", mqttClientExecutor=" + mqttClientExecutor + ", defaultCLIProperties=" + - defaultCLIProperties + '}'; + return "ShellDisconnectCommand{" + + "disconnectOptions=" + + disconnectOptions + + ", defaultOptions=" + + defaultOptions + + ", mqttClientExecutor=" + + mqttClientExecutor + + ", defaultCLIProperties=" + + defaultCLIProperties + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java index ec207c36d..1f044f229 100644 --- a/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java +++ b/src/main/java/com/hivemq/cli/commands/shell/VersionCommand.java @@ -23,8 +23,9 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "version", description = "Prints version information", - versionProvider = MqttCLIMain.CLIVersionProvider.class) +@CommandLine.Command(name = "version", + description = "Prints version information", + versionProvider = MqttCLIMain.CLIVersionProvider.class) public class VersionCommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) diff --git a/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java b/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java index 01c4608de..4b67f1273 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/SwarmCLICommand.java @@ -23,10 +23,14 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "swarm", description = "HiveMQ Swarm Command Line Interpreter.", - synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, - mixinStandardHelpOptions = true) +@CommandLine.Command(name = "swarm", + description = "HiveMQ Swarm Command Line Interpreter.", + synopsisHeading = "%n@|bold Usage:|@ ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmCLICommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @@ -34,7 +38,8 @@ public class SwarmCLICommand implements Callable { private @NotNull CommandLine.Model.CommandSpec spec; @Inject - public SwarmCLICommand() {} + public SwarmCLICommand() { + } @Override public @NotNull Integer call() { diff --git a/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java index 48cebbad0..abd93a55f 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/commander/SwarmStatusCommand.java @@ -37,10 +37,13 @@ import java.util.concurrent.Callable; @CommandLine.Command(name = "status", - description = "Check the status of HiveMQ Swarm. (READY, STARTING, RUNNING, STOPPING).", - synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, - mixinStandardHelpOptions = true) + description = "Check the status of HiveMQ Swarm. (READY, STARTING, RUNNING, STOPPING).", + synopsisHeading = "%n@|bold Usage:|@ ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmStatusCommand implements Callable { public enum OutputFormat { @@ -49,8 +52,9 @@ public enum OutputFormat { } @SuppressWarnings("FieldMayBeFinal") - @CommandLine.Option(names = {"--format"}, defaultValue = "pretty", - description = "The export output format (JSON, PRETTY). Default=PRETTY.", order = 2) + @CommandLine.Option(names = {"--format"}, + defaultValue = "pretty", + description = "The export output format (JSON, PRETTY). Default=PRETTY.") private @NotNull OutputFormat format = OutputFormat.PRETTY; @CommandLine.Mixin @@ -122,15 +126,15 @@ public SwarmStatusCommand( return -1; } if (format == OutputFormat.JSON) { - final CommanderStatusWithRun commanderStatusWithRun = new CommanderStatusWithRun( - commanderStatus.getCommanderStatus(), - runId, - runResponse.getScenarioId(), - runResponse.getScenarioName(), - runResponse.getScenarioDescription(), - runResponse.getScenarioType(), - runResponse.getRunStatus(), - runResponse.getScenarioStage()); + final CommanderStatusWithRun commanderStatusWithRun = + new CommanderStatusWithRun(commanderStatus.getCommanderStatus(), + runId, + runResponse.getScenarioId(), + runResponse.getScenarioName(), + runResponse.getScenarioDescription(), + runResponse.getScenarioType(), + runResponse.getRunStatus(), + runResponse.getScenarioStage()); out.println(gson.toJson(commanderStatusWithRun)); } else if (format == OutputFormat.PRETTY) { out.println("Status: " + commanderStatus.getCommanderStatus()); @@ -156,8 +160,21 @@ public SwarmStatusCommand( @Override public @NotNull String toString() { - return "SwarmStatusCommand{" + "format=" + format + ", swarmOptions=" + swarmOptions + ", gson=" + gson + - ", runsApi=" + runsApi + ", commanderApi=" + commanderApi + ", errorTransformer=" + errorTransformer + - ", out=" + out + '}'; + return "SwarmStatusCommand{" + + "format=" + + format + + ", swarmOptions=" + + swarmOptions + + ", gson=" + + gson + + ", runsApi=" + + runsApi + + ", commanderApi=" + + commanderApi + + ", errorTransformer=" + + errorTransformer + + ", out=" + + out + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/error/SwarmApiErrorTransformer.java b/src/main/java/com/hivemq/cli/commands/swarm/error/SwarmApiErrorTransformer.java index d3b4b122e..76601de1a 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/error/SwarmApiErrorTransformer.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/error/SwarmApiErrorTransformer.java @@ -35,8 +35,7 @@ public SwarmApiErrorTransformer(final @NotNull Gson gson) { public @NotNull Error transformError(final @NotNull ApiException apiException) { if (Logger.isDebugEnabled()) { - Logger.debug( - "Error Code: {}\nError Message: {}", + Logger.debug("Error Code: {}\nError Message: {}", apiException.getCode(), apiException.getMessage(), apiException); diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmOptions.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmOptions.java index dc92a4611..c04d02e5f 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmOptions.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmOptions.java @@ -23,13 +23,15 @@ public class SwarmOptions { - @CommandLine.Option(names = {"-url"}, defaultValue = "http://localhost:8080", - description = "The URL of the HiveMQ Swarm REST API endpoint (default: http://localhost:8080)", order = 1) + @CommandLine.Option(names = {"-url"}, + defaultValue = "http://localhost:8080", + description = "The URL of the HiveMQ Swarm REST API endpoint (default: http://localhost:8080)") private @NotNull String commanderUrl = "http://localhost:8080"; @SuppressWarnings("unused") - @CommandLine.Option(names = {"-l"}, defaultValue = "false", - description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") + @CommandLine.Option(names = {"-l"}, + defaultValue = "false", + description = "Log to $HOME/.mqtt.cli/logs (Configurable through $HOME/.mqtt-cli/config.properties)") private void initLogging(final boolean logToLogfile) { LoggerUtils.turnOffConsoleLogging(logToLogfile); } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java index 984344905..01cb611c9 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunCommand.java @@ -23,10 +23,14 @@ import javax.inject.Inject; import java.util.concurrent.Callable; -@CommandLine.Command(name = "run", description = "HiveMQ Swarm Run Command Line Interpreter.", - synopsisHeading = "%n@|bold Usage:|@ ", descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, - mixinStandardHelpOptions = true) +@CommandLine.Command(name = "run", + description = "HiveMQ Swarm Run Command Line Interpreter.", + synopsisHeading = "%n@|bold Usage:|@ ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmRunCommand implements Callable { @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) @@ -37,7 +41,8 @@ public class SwarmRunCommand implements Callable { private final @NotNull SwarmOptions swarmOptions = new SwarmOptions(); @Inject - public SwarmRunCommand() {} + public SwarmRunCommand() { + } @Override public @NotNull Integer call() throws Exception { diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java index 84794d868..b485dde5b 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStartCommand.java @@ -20,7 +20,13 @@ import com.hivemq.cli.commands.swarm.error.Error; import com.hivemq.cli.commands.swarm.error.SwarmApiErrorTransformer; import com.hivemq.cli.openapi.ApiException; -import com.hivemq.cli.openapi.swarm.*; +import com.hivemq.cli.openapi.swarm.RunResponse; +import com.hivemq.cli.openapi.swarm.RunsApi; +import com.hivemq.cli.openapi.swarm.ScenariosApi; +import com.hivemq.cli.openapi.swarm.StartRunRequest; +import com.hivemq.cli.openapi.swarm.StartRunResponse; +import com.hivemq.cli.openapi.swarm.UploadScenarioRequest; +import com.hivemq.cli.openapi.swarm.UploadScenarioResponse; import okhttp3.HttpUrl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,21 +45,27 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; -@CommandLine.Command(name = "start", description = "Start HiveMQ Swarm runs.", synopsisHeading = "%n@|bold Usage:|@ ", - descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, - mixinStandardHelpOptions = true) +@CommandLine.Command(name = "start", + description = "Start HiveMQ Swarm runs.", + synopsisHeading = "%n@|bold Usage:|@ ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmRunStartCommand implements Callable { - @CommandLine.Option(names = {"-f", "--file"}, description = "The scenario file. " + - "If a scenario file is given this command uploads, executes and deletes the scenario afterwards.", - required = true, order = 3) + @CommandLine.Option(names = {"-f", "--file"}, + description = "The scenario file. " + + "If a scenario file is given this command uploads, executes and deletes the scenario afterwards.", + required = true) private @Nullable File scenario; - @CommandLine.Option(names = {"-d", "--detach"}, defaultValue = "false", - description = "Run the command in detached mode. " + - "In detached mode the command uploads and executes the scenario and does not wait until the scenario is finished. " + - "The scenario is not deleted afterwards.", order = 4) + @CommandLine.Option(names = {"-d", "--detach"}, + defaultValue = "false", + description = "Run the command in detached mode. " + + "In detached mode the command uploads and executes the scenario and does not wait until the scenario is finished. " + + "The scenario is not deleted afterwards.") private @NotNull Boolean detached = false; @CommandLine.Mixin @@ -223,7 +235,8 @@ private void pollUntilFinished(final int runId) throws ApiException { } private boolean isTerminated(final @NotNull RunResponse run) { - return "FINISHED".equals(run.getRunStatus()) || "STOPPED".equals(run.getRunStatus()) || + return "FINISHED".equals(run.getRunStatus()) || + "STOPPED".equals(run.getRunStatus()) || "FAILED".equals(run.getRunStatus()); } @@ -251,8 +264,21 @@ private boolean isTerminated(final @NotNull RunResponse run) { @Override public @NotNull String toString() { - return "SwarmRunStartCommand{" + "scenario=" + scenario + ", detached=" + detached + ", swarmOptions=" + - swarmOptions + ", runsApi=" + runsApi + ", scenariosApi=" + scenariosApi + ", errorTransformer=" + - errorTransformer + ", out=" + out + '}'; + return "SwarmRunStartCommand{" + + "scenario=" + + scenario + + ", detached=" + + detached + + ", swarmOptions=" + + swarmOptions + + ", runsApi=" + + runsApi + + ", scenariosApi=" + + scenariosApi + + ", errorTransformer=" + + errorTransformer + + ", out=" + + out + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java index 3e3c3b628..069717d1b 100644 --- a/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java +++ b/src/main/java/com/hivemq/cli/commands/swarm/run/SwarmRunStopCommand.java @@ -36,14 +36,18 @@ import java.io.PrintStream; import java.util.concurrent.Callable; -@CommandLine.Command(name = "stop", description = "Stop HiveMQ Swarm runs.", synopsisHeading = "%n@|bold Usage:|@ ", - descriptionHeading = "%n", optionListHeading = "%n@|bold Options:|@%n", - commandListHeading = "%n@|bold Commands:|@%n", versionProvider = MqttCLIMain.CLIVersionProvider.class, - mixinStandardHelpOptions = true) +@CommandLine.Command(name = "stop", + description = "Stop HiveMQ Swarm runs.", + synopsisHeading = "%n@|bold Usage:|@ ", + descriptionHeading = "%n", + optionListHeading = "%n@|bold Options:|@%n", + commandListHeading = "%n@|bold Commands:|@%n", + versionProvider = MqttCLIMain.CLIVersionProvider.class, + mixinStandardHelpOptions = true) public class SwarmRunStopCommand implements Callable { @CommandLine.Option(names = {"-r", "--run-id"}, - description = "The id of the run to stop. If none is given the current run is stopped.", order = 3) + description = "The id of the run to stop. If none is given the current run is stopped.") private @Nullable Integer runId; @CommandLine.Mixin @@ -129,7 +133,19 @@ public SwarmRunStopCommand( @Override public @NotNull String toString() { - return "SwarmRunStopCommand{" + "runId=" + runId + ", swarmOptions=" + swarmOptions + ", runsApi=" + runsApi + - ", commanderApi=" + commanderApi + ", errorTransformer=" + errorTransformer + ", out=" + out + '}'; + return "SwarmRunStopCommand{" + + "runId=" + + runId + + ", swarmOptions=" + + swarmOptions + + ", runsApi=" + + runsApi + + ", commanderApi=" + + commanderApi + + ", errorTransformer=" + + errorTransformer + + ", out=" + + out + + '}'; } } diff --git a/src/main/java/com/hivemq/cli/converters/FileToPrivateKeyConverter.java b/src/main/java/com/hivemq/cli/converters/FileToPrivateKeyConverter.java index b551dcdaf..f64760f3e 100644 --- a/src/main/java/com/hivemq/cli/converters/FileToPrivateKeyConverter.java +++ b/src/main/java/com/hivemq/cli/converters/FileToPrivateKeyConverter.java @@ -19,7 +19,11 @@ import com.hivemq.cli.utils.PasswordUtils; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.*; +import org.bouncycastle.openssl.PEMDecryptorProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMException; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; @@ -84,4 +88,4 @@ public class FileToPrivateKeyConverter implements CommandLine.ITypeConverter { static final @NotNull String KEY_VALUE_DELIMETER_ERROR = "a key value pair wasn't delimited by '='"; - static final @NotNull String NO_PAIR_FOUND = "No key value pair was given."; @Override public @NotNull Mqtt5UserProperty convert(final @NotNull String s) throws Exception { @@ -35,5 +34,4 @@ public class Mqtt5UserPropertyConverter implements CommandLine.ITypeConverter clientKeyToClientData = new ConcurrentHashMap<>(); + private static final @NotNull Map clientKeyToClientData = new ConcurrentHashMap<>(); abstract void mqtt5Connect( final @NotNull Mqtt5Client client, final @NotNull Mqtt5Connect connectMessage); @@ -86,27 +96,27 @@ abstract void mqtt3Publish( final @NotNull String topic, final @NotNull MqttQos qos); - abstract void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull UnsubscribeOptions unsubscribeOptions); + abstract void mqtt5Unsubscribe( + final @NotNull Mqtt5Client client, final @NotNull UnsubscribeOptions unsubscribeOptions); - abstract void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull UnsubscribeOptions unsubscribeOptions); + abstract void mqtt3Unsubscribe( + final @NotNull Mqtt3Client client, final @NotNull UnsubscribeOptions unsubscribeOptions); abstract void mqtt5Disconnect( - final @NotNull Mqtt5Client client, - final @NotNull DisconnectOptions disconnectOptions); + final @NotNull Mqtt5Client client, final @NotNull DisconnectOptions disconnectOptions); abstract void mqtt3Disconnect( - final @NotNull Mqtt3Client client, - final @NotNull DisconnectOptions disconnectOptions); + final @NotNull Mqtt3Client client, final @NotNull DisconnectOptions disconnectOptions); public @NotNull MqttClient connect(final @NotNull ConnectOptions connectOptions) throws Exception { return connect(connectOptions, null); } public @NotNull MqttClient connect( - final @NotNull ConnectOptions connectOptions, - final @Nullable SubscribeOptions subscribeOptions) throws Exception { + final @NotNull ConnectOptions connectOptions, final @Nullable SubscribeOptions subscribeOptions) + throws Exception { - final String clientKey = ClientKey.of(connectOptions.getIdentifier(), connectOptions.getHost()).toString(); + final ClientKey clientKey = ClientKey.of(connectOptions.getIdentifier(), connectOptions.getHost()); if (isConnected(clientKey)) { Logger.debug("Client is already connected ({})", clientKey); Logger.info("Using already connected ({})", clientKey); @@ -120,21 +130,18 @@ abstract void mqtt3Disconnect( return connectMqtt3Client(connectOptions, subscribeOptions); } - throw new IllegalStateException( - "The MQTT Version specified is not supported. Version was " + connectOptions.getVersion()); + throw new IllegalStateException("The MQTT Version specified is not supported. Version was " + + connectOptions.getVersion()); } public void subscribe(final @NotNull MqttClient client, final @NotNull SubscribeOptions subscribeOptions) { for (int i = 0; i < subscribeOptions.getTopics().length; i++) { final String topic = subscribeOptions.getTopics()[i]; - final String key = MqttUtils.buildKey(client.getConfig().getClientIdentifier().map(Object::toString).orElse(""), - client.getConfig().getServerHost()); - // This check only works as subscribes are implemented blocking. // Otherwise, we would need to check the topics before they are iterated as they are added to the client data after a successful subscribe. final List intersectingFilters = - checkForSharedTopicDuplicate(clientKeyToClientData.get(ClientKey.of(client).toString()).getSubscribedTopics(), + checkForSharedTopicDuplicate(clientKeyToClientData.get(ClientKey.of(client)).getSubscribedTopics(), topic); // Client{clientIdentifier='hmq_RcrDi_18591249_30a12e2c4bbd17e322a300a4257d76bd', hostname='broker.hivemq.com'} -> {ClientData@3806} // Client{clientIdentifier='hmq_RcrDi_18591249_30a12e2c4bbd17e322a300a4257d76bd', hostname='broker.hivemq.com'} @@ -204,7 +211,7 @@ public void publish(final @NotNull MqttClient client, final @NotNull PublishOpti } public void disconnect(final @NotNull ClientKey clientKey, final @NotNull DisconnectOptions disconnectOptions) { - final ClientData clientData = clientKeyToClientData.get(clientKey.toString()); + final ClientData clientData = clientKeyToClientData.get(clientKey); if (clientData != null) { disconnect(clientData.getClient(), disconnectOptions); } @@ -219,11 +226,11 @@ public void disconnect(final @NotNull MqttClient client, final @NotNull Disconne mqtt3Disconnect((Mqtt3Client) client, disconnectOptions); break; } - clientKeyToClientData.remove(ClientKey.of(client).toString()); + clientKeyToClientData.remove(ClientKey.of(client)); } public void disconnectAllClients(final @NotNull DisconnectOptions disconnectOptions) { - for (final Map.Entry entry : clientKeyToClientData.entrySet()) { + for (final Map.Entry entry : clientKeyToClientData.entrySet()) { final MqttClient client = entry.getValue().getClient(); switch (client.getConfig().getMqttVersion()) { case MQTT_5_0: @@ -249,7 +256,7 @@ public void unsubscribe(final @NotNull MqttClient client, final @NotNull Unsubsc } - public boolean isConnected(final @NotNull String key) { + public boolean isConnected(final @NotNull ClientKey key) { if (clientKeyToClientData.containsKey(key)) { final MqttClient client = clientKeyToClientData.get(key).getClient(); final MqttClientState state = client.getState(); @@ -305,9 +312,7 @@ public boolean isConnected(final @NotNull String key) { final ClientData clientData = new ClientData(client); - final String key = ClientKey.of(client).toString(); - - clientKeyToClientData.put(key, clientData); + clientKeyToClientData.put(ClientKey.of(client), clientData); return client; } @@ -342,10 +347,7 @@ public boolean isConnected(final @NotNull String key) { final ClientData clientData = new ClientData(client); - final String key = MqttUtils.buildKey(client.getConfig().getClientIdentifier().map(Object::toString).orElse(""), - client.getConfig().getServerHost()); - - clientKeyToClientData.put(key, clientData); + clientKeyToClientData.put(ClientKey.of(client), clientData); return client; } @@ -483,15 +485,15 @@ public boolean isConnected(final @NotNull String key) { return null; } - public static @NotNull Map getClientDataMap() { + public static @NotNull Map getClientDataMap() { return clientKeyToClientData; } public @Nullable MqttClient getMqttClient(final @NotNull ClientKey clientKey) { MqttClient client = null; - if (clientKeyToClientData.containsKey(clientKey.toString())) { - client = clientKeyToClientData.get(clientKey.toString()).getClient(); + if (clientKeyToClientData.containsKey(clientKey)) { + client = clientKeyToClientData.get(clientKey).getClient(); } return client; diff --git a/src/main/java/com/hivemq/cli/mqtt/ClientKey.java b/src/main/java/com/hivemq/cli/mqtt/ClientKey.java index 8174c4364..5ad8f4c79 100644 --- a/src/main/java/com/hivemq/cli/mqtt/ClientKey.java +++ b/src/main/java/com/hivemq/cli/mqtt/ClientKey.java @@ -16,6 +16,7 @@ package com.hivemq.cli.mqtt; import com.hivemq.client.mqtt.MqttClient; +import com.hivemq.client.mqtt.MqttClientConfig; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,9 +41,30 @@ public static ClientKey of(final @NotNull MqttClient client) { client.getConfig().getServerHost()); } + public static ClientKey of(final @NotNull MqttClientConfig clientConfig) { + return new ClientKey(clientConfig.getClientIdentifier().map(Objects::toString).orElse(""), + clientConfig.getServerHost()); + } + + @Override + public boolean equals(final @Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ClientKey clientKey = (ClientKey) o; + return Objects.equals(clientIdentifier, clientKey.clientIdentifier) && hostname.equals(clientKey.hostname); + } + + @Override + public int hashCode() { + return Objects.hash(clientIdentifier, hostname); + } @Override - public String toString() { - return "Client{" + "clientIdentifier='" + clientIdentifier + '\'' + ", hostname='" + hostname + '\'' + '}'; + public @NotNull String toString() { + return "client{" + "clientIdentifier='" + clientIdentifier + '\'' + ", hostname='" + hostname + '\'' + '}'; } } diff --git a/src/main/java/com/hivemq/cli/mqtt/ContextClientDisconnectListener.java b/src/main/java/com/hivemq/cli/mqtt/ContextClientDisconnectListener.java index 6241beb59..1e0a489ba 100644 --- a/src/main/java/com/hivemq/cli/mqtt/ContextClientDisconnectListener.java +++ b/src/main/java/com/hivemq/cli/mqtt/ContextClientDisconnectListener.java @@ -20,7 +20,6 @@ import com.hivemq.cli.commands.shell.ShellCommand; import com.hivemq.cli.commands.shell.ShellContextCommand; import com.hivemq.cli.utils.LoggerUtils; -import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.MqttClientConfig; import com.hivemq.client.mqtt.lifecycle.MqttClientDisconnectedContext; import com.hivemq.client.mqtt.lifecycle.MqttClientDisconnectedListener; @@ -52,12 +51,7 @@ public void onDisconnected(final @NotNull MqttClientDisconnectedContext context) } else if (contextEqualsShellContext(context)) { ShellContextCommand.removeContext(); } - MqttClientExecutor.getClientDataMap().remove(getKeyFromConfig(context.getClientConfig())); - } - - private @NotNull String getKeyFromConfig(final @NotNull MqttClientConfig clientConfig) { - return MqttUtils.buildKey(clientConfig.getClientIdentifier().map(Object::toString).orElse(""), - clientConfig.getServerHost()); + MqttClientExecutor.getClientDataMap().remove(ClientKey.of(context.getClientConfig())); } private boolean contextEqualsShellContext(final @NotNull MqttClientDisconnectedContext context) { @@ -68,4 +62,4 @@ private boolean contextEqualsShellContext(final @NotNull MqttClientDisconnectedC return clientConfig.getClientIdentifier().equals(shellClientConfig.getClientIdentifier()) && clientConfig.getServerHost().equals(shellClientConfig.getServerHost()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java index 638aa9f51..25f8e6da3 100644 --- a/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java +++ b/src/main/java/com/hivemq/cli/mqtt/MqttClientExecutor.java @@ -22,7 +22,6 @@ import com.hivemq.cli.commands.options.SubscribeOptions; import com.hivemq.cli.commands.options.UnsubscribeOptions; import com.hivemq.cli.utils.LoggerUtils; -import com.hivemq.cli.utils.MqttUtils; import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.datatypes.MqttTopicFilter; import com.hivemq.client.mqtt.mqtt3.Mqtt3Client; @@ -43,7 +42,6 @@ import com.hivemq.client.mqtt.mqtt5.message.subscribe.Mqtt5Subscribe; import com.hivemq.client.mqtt.mqtt5.message.subscribe.Mqtt5SubscribeBuilder; import com.hivemq.client.mqtt.mqtt5.message.unsubscribe.Mqtt5Unsubscribe; -import com.hivemq.client.mqtt.mqtt5.message.unsubscribe.unsuback.Mqtt5UnsubAck; import org.jetbrains.annotations.NotNull; import org.tinylog.Logger; @@ -56,7 +54,8 @@ public class MqttClientExecutor extends AbstractMqttClientExecutor { @Inject - MqttClientExecutor() {} + MqttClientExecutor() { + } void mqtt5Connect( final @NotNull Mqtt5Client client, final @NotNull Mqtt5Connect connectMessage) { @@ -101,14 +100,13 @@ void mqtt5Subscribe( .subscribe(subscribeMessage, new SubscribeMqtt5PublishCallback(subscribeOptions, client)) .whenComplete((subAck, throwable) -> { if (throwable != null) { - Logger.error( - throwable, + Logger.error(throwable, "{} failed SUBSCRIBE to TOPIC '{}': {}", clientLogPrefix, topic, Throwables.getRootCause(throwable).getMessage()); } else { - final String clientKey = ClientKey.of(client).toString(); + final ClientKey clientKey = ClientKey.of(client); getClientDataMap().get(clientKey).addSubscription(MqttTopicFilter.of(topic)); Logger.debug("{} received SUBACK {}", clientLogPrefix, subAck); } @@ -131,18 +129,13 @@ void mqtt3Subscribe( .subscribe(subscribeMessage, new SubscribeMqtt3PublishCallback(subscribeOptions, client)) .whenComplete((subAck, throwable) -> { if (throwable != null) { - Logger.error( - throwable, + Logger.error(throwable, "{} failed SUBSCRIBE to TOPIC '{}': {}", clientLogPrefix, topic, Throwables.getRootCause(throwable).getMessage()); } else { - final String clientKey = MqttUtils.buildKey( - client.getConfig().getClientIdentifier().map(Object::toString).orElse(""), - client.getConfig().getServerHost()); - - getClientDataMap().get(clientKey).addSubscription(MqttTopicFilter.of(topic)); + getClientDataMap().get(ClientKey.of(client)).addSubscription(MqttTopicFilter.of(topic)); Logger.debug("{} received SUBACK {}", clientLogPrefix, subAck); } @@ -181,16 +174,14 @@ void mqtt5Publish( final Mqtt5Publish publishMessage = publishBuilder.build(); - Logger.debug( - "{} sending PUBLISH ('{}') {}", + Logger.debug("{} sending PUBLISH ('{}') {}", clientLogPrefix, bufferToString(publishOptions.getMessage()), publishMessage); client.toAsync().publish(publishMessage).whenComplete((publishResult, throwable) -> { if (throwable != null) { - Logger.error( - throwable, + Logger.error(throwable, "{} failed PUBLISH to TOPIC '{}': {}", clientLogPrefix, topic, @@ -218,16 +209,14 @@ void mqtt3Publish( final Mqtt3Publish publishMessage = publishBuilder.build(); - Logger.debug( - "{} sending PUBLISH ('{}') {}", + Logger.debug("{} sending PUBLISH ('{}') {}", clientLogPrefix, bufferToString(publishOptions.getMessage()), publishMessage); client.toAsync().publish(publishMessage).whenComplete((publishResult, throwable) -> { if (throwable != null) { - Logger.error( - throwable, + Logger.error(throwable, "{} failed PUBLISH to TOPIC '{}': {}", clientLogPrefix, topic, @@ -250,25 +239,20 @@ void mqtt5Unsubscribe(final @NotNull Mqtt5Client client, final @NotNull Unsubscr Logger.debug("{} sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); - client.toAsync() - .unsubscribe(unsubscribeMessage) - .whenComplete((Mqtt5UnsubAck unsubAck, Throwable throwable) -> { - if (throwable != null) { - - Logger.error( - throwable, - "{} failed UNSUBSCRIBE from TOPIC '{}': {}", - clientLogPrefix, - topic, - Throwables.getRootCause(throwable).getMessage()); - } else { - getClientDataMap().get(ClientKey.of(client).toString()) - .removeSubscription(MqttTopicFilter.of(topic)); - - Logger.debug("{} received UNSUBACK {}", clientLogPrefix, unsubAck); - } - }) - .join(); + client.toAsync().unsubscribe(unsubscribeMessage).whenComplete((unsubAck, throwable) -> { + if (throwable != null) { + + Logger.error(throwable, + "{} failed UNSUBSCRIBE from TOPIC '{}': {}", + clientLogPrefix, + topic, + Throwables.getRootCause(throwable).getMessage()); + } else { + getClientDataMap().get(ClientKey.of(client)).removeSubscription(MqttTopicFilter.of(topic)); + + Logger.debug("{} received UNSUBACK {}", clientLogPrefix, unsubAck); + } + }).join(); } } @@ -279,19 +263,17 @@ void mqtt3Unsubscribe(final @NotNull Mqtt3Client client, final @NotNull Unsubscr for (final String topic : unsubscribeOptions.getTopics()) { final Mqtt3Unsubscribe unsubscribeMessage = Mqtt3Unsubscribe.builder().topicFilter(topic).build(); - Logger.debug("{} Sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); + Logger.debug("{} sending UNSUBSCRIBE {}", clientLogPrefix, unsubscribeMessage); - client.toAsync().unsubscribe(unsubscribeMessage).whenComplete((Void unsubAck, Throwable throwable) -> { + client.toAsync().unsubscribe(unsubscribeMessage).whenComplete((unsubAck, throwable) -> { if (throwable != null) { - Logger.error( - throwable, + Logger.error(throwable, "{} failed UNSUBSCRIBE from TOPIC '{}': {}", clientLogPrefix, topic, Throwables.getRootCause(throwable).getMessage()); } else { - getClientDataMap().get(ClientKey.of(client).toString()) - .removeSubscription(MqttTopicFilter.of(topic)); + getClientDataMap().get(ClientKey.of(client)).removeSubscription(MqttTopicFilter.of(topic)); Logger.debug("{} received UNSUBACK", clientLogPrefix); } }).join(); @@ -325,7 +307,7 @@ void mqtt5Disconnect(final @NotNull Mqtt5Client client, final @NotNull Disconnec @Override void mqtt3Disconnect(final @NotNull Mqtt3Client client, final @NotNull DisconnectOptions disconnectOptions) { - Logger.debug("{} Sending DISCONNECT", LoggerUtils.getClientPrefix(client.getConfig())); + Logger.debug("{} sending DISCONNECT", LoggerUtils.getClientPrefix(client.getConfig())); client.toBlocking().disconnect(); } diff --git a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java index 814211255..7d0ba4401 100644 --- a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java +++ b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt3PublishCallback.java @@ -70,8 +70,7 @@ public void accept(final @NotNull Mqtt3Publish mqtt3Publish) { System.out.println(message); } - Logger.debug( - "{} received PUBLISH ('{}') {}", + Logger.debug("{} received PUBLISH ('{}') {}", LoggerUtils.getClientPrefix(client.getConfig()), new String(mqtt3Publish.getPayloadAsBytes(), StandardCharsets.UTF_8), mqtt3Publish); diff --git a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java index 912d01667..6b37a4c51 100644 --- a/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java +++ b/src/main/java/com/hivemq/cli/mqtt/SubscribeMqtt5PublishCallback.java @@ -70,8 +70,7 @@ public void accept(final @NotNull Mqtt5Publish mqtt5Publish) { System.out.println(message); } - Logger.debug( - "{} received PUBLISH ('{}') {}", + Logger.debug("{} received PUBLISH ('{}') {}", LoggerUtils.getClientPrefix(client.getConfig()), new String(mqtt5Publish.getPayloadAsBytes(), StandardCharsets.UTF_8), mqtt5Publish); diff --git a/src/main/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTester.java b/src/main/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTester.java index 6dd11693d..5c35ca348 100644 --- a/src/main/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTester.java +++ b/src/main/java/com/hivemq/cli/mqtt/test/Mqtt3FeatureTester.java @@ -17,7 +17,14 @@ package com.hivemq.cli.mqtt.test; import com.google.common.base.Strings; -import com.hivemq.cli.mqtt.test.results.*; +import com.hivemq.cli.mqtt.test.results.AsciiCharsInClientIdTestResults; +import com.hivemq.cli.mqtt.test.results.ClientIdLengthTestResults; +import com.hivemq.cli.mqtt.test.results.PayloadTestResults; +import com.hivemq.cli.mqtt.test.results.QosTestResult; +import com.hivemq.cli.mqtt.test.results.SharedSubscriptionTestResult; +import com.hivemq.cli.mqtt.test.results.TestResult; +import com.hivemq.cli.mqtt.test.results.TopicLengthTestResults; +import com.hivemq.cli.mqtt.test.results.WildcardSubscriptionsTestResult; import com.hivemq.cli.utils.TopicUtils; import com.hivemq.cli.utils.Tuple; import com.hivemq.client.mqtt.MqttClientSslConfig; @@ -38,7 +45,11 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -688,7 +699,9 @@ public void setMaxTopicLength(final int topicLength) { maxTopicLength = topicLength; } - public void setMaxQos(final @NotNull MqttQos qos) {maxQos = qos;} + public void setMaxQos(final @NotNull MqttQos qos) { + maxQos = qos; + } // Helpers private @NotNull Mqtt3Client buildClient() { diff --git a/src/main/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTester.java b/src/main/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTester.java index f702912c3..a831f7035 100644 --- a/src/main/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTester.java +++ b/src/main/java/com/hivemq/cli/mqtt/test/Mqtt5FeatureTester.java @@ -17,7 +17,14 @@ package com.hivemq.cli.mqtt.test; import com.google.common.base.Strings; -import com.hivemq.cli.mqtt.test.results.*; +import com.hivemq.cli.mqtt.test.results.AsciiCharsInClientIdTestResults; +import com.hivemq.cli.mqtt.test.results.ClientIdLengthTestResults; +import com.hivemq.cli.mqtt.test.results.PayloadTestResults; +import com.hivemq.cli.mqtt.test.results.QosTestResult; +import com.hivemq.cli.mqtt.test.results.SharedSubscriptionTestResult; +import com.hivemq.cli.mqtt.test.results.TestResult; +import com.hivemq.cli.mqtt.test.results.TopicLengthTestResults; +import com.hivemq.cli.mqtt.test.results.WildcardSubscriptionsTestResult; import com.hivemq.cli.utils.TopicUtils; import com.hivemq.cli.utils.Tuple; import com.hivemq.client.mqtt.MqttClientSslConfig; @@ -40,7 +47,11 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -222,8 +233,7 @@ public Mqtt5FeatureTester( for (int i = 0; i < tries; i++) { try { - Logger.trace( - "Publishing message {} to topic {} with qos {}", + Logger.trace("Publishing message {} to topic {} with qos {}", new String(payload, StandardCharsets.UTF_8), topic, qos); @@ -369,8 +379,7 @@ public Mqtt5FeatureTester( try { countDownLatch.await(timeOut, TimeUnit.SECONDS); } catch (final InterruptedException e) { - Logger.error( - e, + Logger.error(e, "Interrupted while subscription to '{}' receives publish to '{}'", subscribeToTopic, publishToTopic); @@ -458,8 +467,7 @@ private boolean testPayload( return false; } } catch (final InterruptedException e) { - Logger.error( - e, + Logger.error(e, "Interrupted while waiting for subscriber to receive payload with length {} bytes", currentPayload.getBytes().length); testResults.add(Tuple.of(payloadSize, TestResult.INTERRUPTED)); @@ -557,8 +565,7 @@ private boolean testTopic(final @NotNull List> testRe return false; } } catch (final InterruptedException e) { - Logger.error( - e, + Logger.error(e, "Interrupted while waiting to receive publish to topic with {} bytes", currentTopicName.getBytes().length); testResults.add(Tuple.of(topicSize, TestResult.INTERRUPTED)); @@ -623,8 +630,7 @@ private boolean testClientIdLength( disconnectIfConnected(currClient); return false; } catch (final Exception ex) { - Logger.error( - ex, + Logger.error(ex, "Connect with client id length {} bytes", currClient.getConfig().getClientIdentifier().map(id -> id.toString().getBytes().length).orElse(0)); connectResults.add(Tuple.of(clientIdLength, "UNDEFINED_FAILURE")); @@ -665,8 +671,7 @@ private boolean testClientIdLength( for (int i = 0; i < ASCII.length(); i++) { testAsciiChar(connectResults, ASCII.charAt(i)); } - Logger.debug( - "Result of testing ascii character in client identifier: Unsupported characters {}", + Logger.debug("Result of testing ascii character in client identifier: Unsupported characters {}", connectResults.toString()); } return new AsciiCharsInClientIdTestResults(connectResults); @@ -694,7 +699,9 @@ public void setMaxTopicLength(final int maxTopicLength) { this.maxTopicLength = maxTopicLength; } - public void setMaxQos(final @NotNull MqttQos qos) {maxQos = qos;} + public void setMaxQos(final @NotNull MqttQos qos) { + maxQos = qos; + } private @NotNull Mqtt5Client buildClient() { return getClientBuilder().build(); diff --git a/src/main/java/com/hivemq/cli/rest/HiveMQRestService.java b/src/main/java/com/hivemq/cli/rest/HiveMQRestService.java index 728674770..47d471043 100644 --- a/src/main/java/com/hivemq/cli/rest/HiveMQRestService.java +++ b/src/main/java/com/hivemq/cli/rest/HiveMQRestService.java @@ -55,9 +55,9 @@ public void getClientDetails( clientsApi.getMqttClientDetailsAsync(clientId, callback); } - public @NotNull ApiClient getApiClient() {return apiClient;} - - public @NotNull MqttClientsApi getClientsApi() {return clientsApi;} + public @NotNull ApiClient getApiClient() { + return apiClient; + } private @NotNull OkHttpClient buildOkHttpClient(final double requestsPerSecondLimit) { return new OkHttpClient.Builder().connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) diff --git a/src/main/java/com/hivemq/cli/utils/IntersectionUtil.java b/src/main/java/com/hivemq/cli/utils/IntersectionUtil.java index 94031adea..f5961eed1 100644 --- a/src/main/java/com/hivemq/cli/utils/IntersectionUtil.java +++ b/src/main/java/com/hivemq/cli/utils/IntersectionUtil.java @@ -29,8 +29,7 @@ public class IntersectionUtil { public static boolean intersects(final @NotNull MqttTopicFilter filterA, final @NotNull MqttTopicFilter filterB) { // Topics are equal (fast evaluation) if (filterA.matches(filterB) || filterB.matches(filterA)) { - Logger.debug( - "Topic filter \"{}\" matches \"{}\" and therefore they intersect.", + Logger.debug("Topic filter \"{}\" matches \"{}\" and therefore they intersect.", filterA.toString(), filterB.toString()); return true; @@ -54,8 +53,7 @@ public static boolean intersects(final @NotNull MqttTopicFilter filterA, final @ if (filterALevelSize > filterBLevelSize) { // FilterB is smaller if (!filterB.containsMultiLevelWildcard()) { - Logger.debug( - "Topic filter \"{}\" has less levels than topic filter \"{}\" " + + Logger.debug("Topic filter \"{}\" has less levels than topic filter \"{}\" " + "and does not contain a multi level wildcard. Therefore, it is disjoint.", filterB.toString(), filterA.toString()); @@ -67,8 +65,7 @@ public static boolean intersects(final @NotNull MqttTopicFilter filterA, final @ final List filterBIntersectedLevels = filterBLevels.subList(0, levelIndex); final String filterADisjointLevel = filterALevels.get(levelIndex); final String filterBDisjointLevel = filterBLevels.get(levelIndex); - Logger.debug( - "Topic filters intersected up to level {} (\"{}\"<=>\"{}\") " + + Logger.debug("Topic filters intersected up to level {} (\"{}\"<=>\"{}\") " + "but disjoint at level {} with \"{}\"<=/=>\"{}\"", levelIndex, String.join("/", filterAIntersectedLevels), @@ -82,8 +79,7 @@ public static boolean intersects(final @NotNull MqttTopicFilter filterA, final @ } } else if (filterALevelSize < filterBLevelSize) { // FilterA is smaller if (!filterA.containsMultiLevelWildcard()) { - Logger.debug( - "Topic filter \"{}\" has less levels than topic filter \"{}\" " + + Logger.debug("Topic filter \"{}\" has less levels than topic filter \"{}\" " + "and does not contain a multi level wildcard. Therefore, it is disjoint.", filterA.toString(), filterB.toString()); @@ -95,8 +91,7 @@ public static boolean intersects(final @NotNull MqttTopicFilter filterA, final @ final List filterBIntersectedLevels = filterBLevels.subList(0, levelIndex); final String filterADisjointLevel = filterALevels.get(levelIndex); final String filterBDisjointLevel = filterBLevels.get(levelIndex); - Logger.debug( - "Topic filters intersected up to level {} (\"{}\"<=>\"{}\") " + + Logger.debug("Topic filters intersected up to level {} (\"{}\"<=>\"{}\") " + "but disjoint at level {} with \"{}\"<=/=>\"{}\"", levelIndex, String.join("/", filterAIntersectedLevels), @@ -115,8 +110,7 @@ public static boolean intersects(final @NotNull MqttTopicFilter filterA, final @ final List filterBIntersectedLevels = filterBLevels.subList(0, levelIndex + 1); final String filterADisjointLevel = filterALevels.get(levelIndex); final String filterBDisjointLevel = filterBLevels.get(levelIndex); - Logger.debug( - "Topic filters intersected up to level {} (\"{}\"<=>\"{}\") " + + Logger.debug("Topic filters intersected up to level {} (\"{}\"<=>\"{}\") " + "but disjoint at level {} with \"{}\"<=/=>\"{}\"", levelIndex, String.join("/", filterAIntersectedLevels), diff --git a/src/main/java/com/hivemq/cli/utils/LoggerUtils.java b/src/main/java/com/hivemq/cli/utils/LoggerUtils.java index 5e1d14a38..b92b41488 100644 --- a/src/main/java/com/hivemq/cli/utils/LoggerUtils.java +++ b/src/main/java/com/hivemq/cli/utils/LoggerUtils.java @@ -31,7 +31,11 @@ import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; public class LoggerUtils { diff --git a/src/main/java/com/hivemq/cli/utils/MqttPublishUtils.java b/src/main/java/com/hivemq/cli/utils/MqttPublishUtils.java index 5a311e757..54d3f75f4 100644 --- a/src/main/java/com/hivemq/cli/utils/MqttPublishUtils.java +++ b/src/main/java/com/hivemq/cli/utils/MqttPublishUtils.java @@ -48,8 +48,7 @@ public static void printToFile(final @NotNull File publishFile, final @NotNull S } try { - Files.write( - publishFile.toPath(), + Files.write(publishFile.toPath(), (message + System.lineSeparator()).getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND); } catch (final @NotNull IOException e) { diff --git a/src/main/java/com/hivemq/cli/utils/MqttUtils.java b/src/main/java/com/hivemq/cli/utils/MqttUtils.java index 3a3a74bfa..fd1b0d8a2 100644 --- a/src/main/java/com/hivemq/cli/utils/MqttUtils.java +++ b/src/main/java/com/hivemq/cli/utils/MqttUtils.java @@ -44,7 +44,8 @@ public enum IdentifierWarning { // The returned qos array will be filled with the default value represented by the first element in the given qos array // if the sizes do not match up. Else this method throws an IllegalArgument exception public static @NotNull MqttQos @NotNull [] arrangeQosToMatchTopics( - final @NotNull String @NotNull [] topics, final @NotNull MqttQos @NotNull [] qos) throws IllegalArgumentException { + final @NotNull String @NotNull [] topics, final @NotNull MqttQos @NotNull [] qos) + throws IllegalArgumentException { if (topics.length != qos.length && qos.length == 1) { final MqttQos defaultQos = qos[0]; final MqttQos[] newQos = new MqttQos[topics.length]; @@ -53,9 +54,11 @@ public enum IdentifierWarning { } else if (topics.length == qos.length) { return qos; } - throw new IllegalArgumentException( - "Topics do not match up to the QoS given. Topics Size {" + topics.length + "}, QoS Size {" + - qos.length + "}"); + throw new IllegalArgumentException("Topics do not match up to the QoS given. Topics Size {" + + topics.length + + "}, QoS Size {" + + qos.length + + "}"); } public static @Nullable Mqtt5UserProperties convertToMqtt5UserProperties(final @Nullable Mqtt5UserProperty @Nullable ... userProperties) { @@ -68,18 +71,6 @@ public enum IdentifierWarning { } } - public static @NotNull Throwable getRootCause(final @NotNull Throwable t) { - Throwable currentThrowable = t; - while (currentThrowable.getCause() != null) { - currentThrowable = currentThrowable.getCause(); - } - return currentThrowable; - } - - public static @NotNull String buildKey(final @NotNull String identifier, final @NotNull String host) { - return "client {" + "identifier='" + identifier + '\'' + ", host='" + host + '\'' + '}'; - } - // See http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.html#_Toc514345331 public static @NotNull String buildRandomClientID(final int length) { if (length < 0) { diff --git a/src/main/resources/META-INF/native-image/reflect-config.json b/src/main/resources/META-INF/native-image/reflect-config.json index 749e7faff..20984e15f 100644 --- a/src/main/resources/META-INF/native-image/reflect-config.json +++ b/src/main/resources/META-INF/native-image/reflect-config.json @@ -1,12 +1,7 @@ [ { - "name":"[B" -}, -{ - "name":"[Ljava.lang.String;" -}, -{ - "name":"[Lsun.security.pkcs.SignerInfo;" + "name":"apple.security.AppleProvider", + "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.hivemq.cli.utils.json.JsonFormatted", @@ -29,137 +24,420 @@ "methods":[{"name":"current","parameterTypes":[] }] }, { - "name":"java.lang.String" + "name":"java.security.SecureRandomParameters" }, { - "name":"java.security.AlgorithmParametersSpi" + "name":"java.security.cert.PKIXRevocationChecker" }, { - "name":"java.security.MessageDigestSpi" + "name":"java.security.spec.DSAParameterSpec" }, { - "name":"java.security.interfaces.DSAPrivateKey" + "name":"java.sql.Date" }, { - "name":"java.security.interfaces.DSAPublicKey" + "name":"org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"java.security.interfaces.RSAPrivateKey" + "name":"org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"java.security.interfaces.RSAPublicKey" + "name":"org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"java.security.spec.DSAParameterSpec" + "name":"org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"java.sql.Date" + "name":"org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"java.util.Date" + "name":"org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"javax.security.auth.x500.X500Principal", - "fields":[{"name":"thisX500Name"}], - "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }] + "name":"org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.tinylog.configuration.PropertiesConfigurationLoader", + "name":"org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.tinylog.core.TinylogLoggingProvider", + "name":"org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.tinylog.writers.ConsoleWriter", - "methods":[{"name":"","parameterTypes":["java.util.Map"] }] + "name":"org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.misc.Unsafe", - "fields":[{"name":"theUnsafe"}], - "queriedMethods":[{"name":"allocateInstance","parameterTypes":["java.lang.Class"] }] + "name":"org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.provider.DSA$SHA1withDSA", + "name":"org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.provider.DSA$SHA256withDSA", + "name":"org.bouncycastle.jcajce.provider.asymmetric.LMS$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.provider.DSAKeyFactory", + "name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.provider.DSAParameters", + "name":"org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.provider.SHA", + "name":"org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.provider.SHA2$SHA256", + "name":"org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.provider.X509Factory", + "name":"org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "name":"org.bouncycastle.jcajce.provider.digest.Blake3$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.rsa.RSASignature$SHA256withRSA", + "name":"org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Haraka$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Keccak$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA224$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.util.ObjectIdentifier" + "name":"org.bouncycastle.jcajce.provider.digest.SHA256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.AuthorityInfoAccessExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.digest.SHA3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.AuthorityKeyIdentifierExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.digest.SHA384$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.BasicConstraintsExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.digest.SHA512$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.CRLDistributionPointsExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.digest.SM3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.CertificateExtensions" + "name":"org.bouncycastle.jcajce.provider.digest.Skein$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.CertificatePoliciesExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.digest.Tiger$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.ExtendedKeyUsageExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.KeyUsageExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.NetscapeCertTypeExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.SubjectAlternativeNameExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.symmetric.AES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"sun.security.x509.SubjectKeyIdentifierExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] + "name":"org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.tinylog.configuration.PropertiesConfigurationLoader", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.tinylog.core.TinylogLoggingProvider", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.tinylog.writers.ConsoleWriter", + "methods":[{"name":"","parameterTypes":["java.util.Map"] }] +}, +{ + "name":"org.tinylog.writers.FileWriter", + "methods":[{"name":"","parameterTypes":["java.util.Map"] }] +}, +{ + "name":"sun.misc.Unsafe", + "fields":[{"name":"theUnsafe"}], + "queriedMethods":[{"name":"allocateInstance","parameterTypes":["java.lang.Class"] }] +}, +{ + "name":"sun.security.provider.DSA$SHA256withDSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA2$SHA256", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSASignature$SHA256withRSA", + "methods":[{"name":"","parameterTypes":[] }] } ] diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index 8dfd35375..0d7ea709c 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -46,6 +46,7 @@ import static com.hivemq.cli.utils.broker.assertions.ConnectAssertion.assertConnectPacket; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class PublishConnectST { @@ -71,7 +72,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { "-d"); final ExecutionResult executionResult = MqttCli.execute(publishCommand); - assertEquals(0, executionResult.getExitCode()); + assertEquals(1, executionResult.getExitCode()); assertTrue( executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); @@ -95,7 +96,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { "-d"); final ExecutionResult executionResult = MqttCli.execute(publishCommand); - assertEquals(0, executionResult.getExitCode()); + assertEquals(1, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput().contains("readAddress(..) failed: Connection reset by peer") || executionResult.getErrorOutput().contains("Connection refused")); } @@ -103,7 +104,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_connectCleanStart(final char mqttVersion) throws Exception { + void test_connectNoCleanStart(final char mqttVersion) throws Exception { final List publishCommand = defaultPublishCommand(mqttVersion); publishCommand.add("--no-cleanStart"); @@ -119,6 +120,26 @@ void test_connectCleanStart(final char mqttVersion) throws Exception { }); } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectCleanStart(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--cleanStart"); + + final ExecutionResult executionResult = MqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); + connectAssertion.setCleanStart(true); + if (mqttVersion == '3') { + connectAssertion.setSessionExpiryInterval(0L); + } + }); + } + + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -224,7 +245,7 @@ void test_connectPassword(final char mqttVersion) throws Exception { if (mqttVersion == '3') { assertTrue(executionResult.getErrorOutput() .contains("Password-Only Authentication is not allowed in MQTT 3")); - assertEquals(0, executionResult.getExitCode()); + assertEquals(1, executionResult.getExitCode()); } else { assertPublishOutput(executionResult); assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { @@ -247,7 +268,7 @@ void test_connectPasswordEnv(final char mqttVersion) throws Exception { if (mqttVersion == '3') { assertTrue(executionResult.getErrorOutput() .contains("Password-Only Authentication is not allowed in MQTT 3")); - assertEquals(0, executionResult.getExitCode()); + assertEquals(1, executionResult.getExitCode()); } else { assertPublishOutput(executionResult); assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { @@ -274,7 +295,7 @@ void test_connectPasswordFile(final char mqttVersion) throws Exception { if (mqttVersion == '3') { assertTrue(executionResult.getErrorOutput() .contains("Password-Only Authentication is not allowed in MQTT 3")); - assertEquals(0, executionResult.getExitCode()); + assertEquals(1, executionResult.getExitCode()); } else { assertPublishOutput(executionResult); assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { @@ -502,7 +523,7 @@ void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_connectRequestProblemInformation(final char mqttVersion) throws Exception { + void test_connectNoRequestProblemInformation(final char mqttVersion) throws Exception { final List publishCommand = defaultPublishCommand(mqttVersion); publishCommand.add("--no-reqProblemInfo"); @@ -510,7 +531,7 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except assertPublishOutput(executionResult); if (mqttVersion == '3') { - assertTrue(executionResult.getErrorOutput() + assertFalse(executionResult.getErrorOutput() .contains("Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1")); } @@ -522,6 +543,29 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except }); } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectRequestProblemInformation(final char mqttVersion) throws Exception { + final List publishCommand = defaultPublishCommand(mqttVersion); + publishCommand.add("--reqProblemInfo"); + + final ExecutionResult executionResult = MqttCli.execute(publishCommand); + assertPublishOutput(executionResult); + + if (mqttVersion == '3') { + assertTrue(executionResult.getErrorOutput() + .contains("Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1")); + } + + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestProblemInformation(true); + } + }); + } + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java index 9c240cb89..fe456abaf 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishST.java @@ -373,7 +373,7 @@ void test_publish_missing_topic() throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(2, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); + assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic='")); } @Test @@ -386,7 +386,7 @@ void test_publish_missing_message() throws Exception { assertEquals(2, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput() - .contains("Error: Missing required argument (specify one of these):")); + .contains("Missing required argument (specify one of these):")); } private void assertPublishOutput(final @NotNull ExecutionResult executionResult) { diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java index 21e74a5ab..72c1ab047 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -74,7 +74,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { "-d"); final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); - assertEquals(0, executionResult.getExitCode()); + assertEquals(1, executionResult.getExitCode()); assertTrue( executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); @@ -96,7 +96,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { "-d"); final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); - assertEquals(0, executionResult.getExitCode()); + assertEquals(1, executionResult.getExitCode()); assertTrue(executionResult.getErrorOutput().contains("readAddress(..) failed: Connection reset by peer") || executionResult.getErrorOutput().contains("Connection refused")); } @@ -104,7 +104,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_connectCleanStart(final char mqttVersion) throws Exception { + void test_connectNoCleanStart(final char mqttVersion) throws Exception { final List subscribeCommand = defaultSubscribeCommand(mqttVersion); subscribeCommand.add("--no-cleanStart"); @@ -120,6 +120,25 @@ void test_connectCleanStart(final char mqttVersion) throws Exception { }); } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectCleanStart(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--cleanStart"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); + connectAssertion.setCleanStart(true); + if (mqttVersion == '3') { + connectAssertion.setSessionExpiryInterval(0L); + } + }); + } + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -492,13 +511,31 @@ void test_connectTopicAliasMaximum(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_connectRequestProblemInformation(final char mqttVersion) throws Exception { + void test_connectNoRequestProblemInformation(final char mqttVersion) throws Exception { final List subscribeCommand = defaultSubscribeCommand(mqttVersion); subscribeCommand.add("--no-reqProblemInfo"); final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); assertSubscribeOutput(executionResult); + assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestProblemInformation(false); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_connectRequestProblemInformation(final char mqttVersion) throws Exception { + final List subscribeCommand = defaultSubscribeCommand(mqttVersion); + subscribeCommand.add("--reqProblemInfo"); + + final ExecutionResultAsync executionResult = mqttCli.executeAsync(subscribeCommand); + assertSubscribeOutput(executionResult); + if (mqttVersion == '3') { executionResult.awaitStdErr( "Restriction request problem information was set but is unused in MQTT Version MQTT_3_1_1"); @@ -507,7 +544,7 @@ void test_connectRequestProblemInformation(final char mqttVersion) throws Except assertConnectPacket(HIVEMQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { - connectAssertion.setRequestProblemInformation(false); + connectAssertion.setRequestProblemInformation(true); } }); } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java index b34385955..b32223925 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeST.java @@ -339,7 +339,7 @@ void test_subscribeMissingTopic() throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(2, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic '")); + assertTrue(executionResult.getErrorOutput().contains("Missing required option: '--topic='")); } private @NotNull List defaultSubscribeCommand(final char mqttVersion) { diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index 3b60e8f7c..ca14b99c7 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -59,7 +59,7 @@ class ShellConnectST { @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_help() throws Exception { final List connectCommand = List.of("con", "--help"); - mqttCliShell.executeAsync(connectCommand).awaitStdOut("Usage").awaitStdOut("Options").awaitStdOut("mqtt>"); + mqttCliShell.executeAsync(connectCommand).awaitStdOut("Usage").awaitStdOut("OPTIONS").awaitStdOut("mqtt>"); } @ParameterizedTest @@ -74,8 +74,7 @@ void test_defaultConnect(final char mqttVersion) throws Exception { .awaitLog("received CONNACK"); final ConnectPacket connectPacket = HIVE_MQ.getConnectPackets().get(0); - assertConnectPacket( - connectPacket, + assertConnectPacket(connectPacket, connectAssertion -> connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion( mqttVersion))); } @@ -84,8 +83,7 @@ void test_defaultConnect(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_connectWhileConnected(final char mqttVersion) throws Exception { - final List connectCommand1 = List.of( - "con", + final List connectCommand1 = List.of("con", "-h", HIVE_MQ.getHost(), "-p", @@ -95,8 +93,7 @@ void test_connectWhileConnected(final char mqttVersion) throws Exception { "-i", "client1"); - final List connectCommand2 = List.of( - "con", + final List connectCommand2 = List.of("con", "-h", HIVE_MQ.getHost(), "-p", @@ -148,8 +145,7 @@ void test_wrongPort(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_wrongHost(final char mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", "unreachable-host", "-p", @@ -170,7 +166,7 @@ void test_wrongHost(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_cleanStart(final char mqttVersion) throws Exception { + void test_noCleanStart(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--no-cleanStart"); @@ -188,6 +184,27 @@ void test_cleanStart(final char mqttVersion) throws Exception { }); } + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_cleanStart(final char mqttVersion) throws Exception { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--cleanStart"); + + mqttCliShell.executeAsync(connectCommand) + .awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())) + .awaitLog("sending CONNECT") + .awaitLog("received CONNACK"); + + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); + if (mqttVersion == '3') { + connectAssertion.setSessionExpiryInterval(0L); + } + connectAssertion.setCleanStart(true); + }); + } + @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) @@ -272,10 +289,31 @@ void test_topicAliasMaximum(final char mqttVersion) throws Exception { @ParameterizedTest @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) - void test_requestProblemInformation(final char mqttVersion) throws IOException { + void test_noRequestProblemInformation(final char mqttVersion) throws IOException { final List connectCommand = defaultConnectCommand(mqttVersion); connectCommand.add("--no-reqProblemInfo"); + final AwaitOutput awaitOutput = + mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); + + awaitOutput.awaitLog("sending CONNECT"); + awaitOutput.awaitLog("received CONNACK"); + + assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { + connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); + if (mqttVersion == '5') { + connectAssertion.setRequestProblemInformation(false); + } + }); + } + + @ParameterizedTest + @Timeout(value = 3, unit = TimeUnit.MINUTES) + @ValueSource(chars = {'3', '5'}) + void test_requestProblemInformation(final char mqttVersion) throws IOException { + final List connectCommand = defaultConnectCommand(mqttVersion); + connectCommand.add("--reqProblemInfo"); + final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand).awaitStdOut(String.format("@%s>", HIVE_MQ.getHost())); @@ -292,7 +330,7 @@ void test_requestProblemInformation(final char mqttVersion) throws IOException { assertConnectPacket(HIVE_MQ.getConnectPackets().get(0), connectAssertion -> { connectAssertion.setMqttVersion(MqttVersionConverter.toExtensionSdkVersion(mqttVersion)); if (mqttVersion == '5') { - connectAssertion.setRequestProblemInformation(false); + connectAssertion.setRequestProblemInformation(true); } }); } @@ -329,8 +367,7 @@ void test_requestResponseInformation(final char mqttVersion) throws IOException @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_noClientId(final char mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", HIVE_MQ.getHost(), "-p", @@ -556,8 +593,7 @@ void test_will(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_sessionExpiryInterval(final char mqttVersion) throws Exception { final String clientId = "sessionTest_V" + mqttVersion; - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", HIVE_MQ.getHost(), "-p", @@ -620,8 +656,7 @@ void test_sessionExpiryInterval(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_identifierPrefix(final char mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", HIVE_MQ.getHost(), "-p", @@ -647,9 +682,9 @@ void test_identifierPrefix(final char mqttVersion) throws Exception { @ValueSource(chars = {'3', '5'}) void test_userProperties(final char mqttVersion) throws Exception { final List connectCommand = defaultConnectCommand(mqttVersion); - connectCommand.add("-up"); + connectCommand.add("-Cup"); connectCommand.add("key1=value1"); - connectCommand.add("-up"); + connectCommand.add("-Cup"); connectCommand.add("key2=value2"); final AwaitOutput awaitOutput = mqttCliShell.executeAsync(connectCommand); @@ -683,8 +718,7 @@ void test_userProperties(final char mqttVersion) throws Exception { @Timeout(value = 3, unit = TimeUnit.MINUTES) @ValueSource(chars = {'3', '5'}) void test_keepAlive(final char mqttVersion) throws Exception { - final List connectCommand = List.of( - "con", + final List connectCommand = List.of("con", "-h", HIVE_MQ.getHost(), "-p", diff --git a/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java index 045dec282..920372812 100644 --- a/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java +++ b/src/systemTest/java/com/hivemq/cli/utils/OrphanProcessCleanup.java @@ -37,7 +37,7 @@ public class OrphanProcessCleanup { String.valueOf(childProcess.pid())); final Process orphanCleanupProcess = new ProcessBuilder(orphanCleanupProcessCommand).start(); - // Wait until the process prints X, which means that the orphan cleanup process has sucessfully started + // Wait until the process prints X, which means that the orphan cleanup process has successfully started final int readChar = orphanCleanupProcess.getInputStream().read(); assertEquals('X', readChar); diff --git a/src/systemTest/resources/OrphanCleanupProcess.java b/src/systemTest/resources/OrphanCleanupProcess.java index f8ac0191f..97d5d8d5e 100644 --- a/src/systemTest/resources/OrphanCleanupProcess.java +++ b/src/systemTest/resources/OrphanCleanupProcess.java @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.hivemq.cli.utils; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -26,11 +26,17 @@ public class OrphanCleanupProcess { public static void main(final String[] args) throws ExecutionException, InterruptedException, TimeoutException { final String jvmProcessId = args[0]; final String childProcessId = args[1]; - final ProcessHandle jvmProcess = ProcessHandle.of(Long.parseLong(jvmProcessId)).get(); - final ProcessHandle childProcess = ProcessHandle.of(Long.parseLong(childProcessId)).get(); - final CompletableFuture future = jvmProcess.onExit().whenComplete((processHandle, throwable) -> { - childProcess.destroyForcibly(); - }); + final Optional jvmProcess = ProcessHandle.of(Long.parseLong(jvmProcessId)); + final Optional childProcess = ProcessHandle.of(Long.parseLong(childProcessId)); + final CompletableFuture future; + if (jvmProcess.isPresent() && childProcess.isPresent()) { + future = jvmProcess.get().onExit().whenComplete((processHandle, throwable) -> childProcess.get().destroyForcibly()); + } else if (jvmProcess.isEmpty() && childProcess.isPresent()) { + childProcess.get().destroyForcibly(); + future = CompletableFuture.completedFuture(null); + } else { + future = CompletableFuture.completedFuture(null); + } System.out.println('X'); future.get(300, TimeUnit.SECONDS); } From 3fa6e87eb1e56bf33d6aad4f4258ed32283f51d7 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Mon, 19 Dec 2022 15:05:42 +0100 Subject: [PATCH 63/64] Fixed connect messaging. --- src/main/java/com/hivemq/cli/utils/LoggerUtils.java | 4 ++-- .../hivemq/cli/commands/cli/publish/PublishConnectST.java | 6 ++---- .../cli/commands/cli/subscribe/SubscribeConnectST.java | 6 ++---- .../hivemq/cli/commands/shell/connect/ShellConnectST.java | 8 ++++---- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/hivemq/cli/utils/LoggerUtils.java b/src/main/java/com/hivemq/cli/utils/LoggerUtils.java index b92b41488..f9b78d4ad 100644 --- a/src/main/java/com/hivemq/cli/utils/LoggerUtils.java +++ b/src/main/java/com/hivemq/cli/utils/LoggerUtils.java @@ -122,7 +122,7 @@ public static void logCommandError( } else { final String exceptionMessage = Throwables.getRootCause(exception).getMessage(); if (exceptionMessage != null) { - Logger.error("{}. {}", message, exceptionMessage); + Logger.error("{}. Reason: '{}'", message, exceptionMessage); } else { Logger.error("{}. Use '-d' option to get more detailed information.", message); } @@ -132,7 +132,7 @@ public static void logCommandError( public static void logShellError(final @NotNull String message, final @NotNull Exception exception) { final String exceptionMessage = Throwables.getRootCause(exception).getMessage(); if (exceptionMessage != null) { - Logger.error(exception, "{}. {}", message, exceptionMessage); + Logger.error(exception, "{}. Reason: '{}'", message, exceptionMessage); } else { Logger.error(exception, "{}. Use 'mqtt sh -l' to see more detailed information in the logfile.", message); } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index 0d7ea709c..c564f29f6 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -74,8 +74,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(1, executionResult.getExitCode()); assertTrue( - executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || - executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); + executionResult.getErrorOutput().contains("Unable to connect.")); } @ParameterizedTest @@ -97,8 +96,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(1, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("readAddress(..) failed: Connection reset by peer") || - executionResult.getErrorOutput().contains("Connection refused")); + assertTrue(executionResult.getErrorOutput().contains("Unable to connect.")); } @ParameterizedTest diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java index 72c1ab047..ba0884893 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -76,8 +76,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(1, executionResult.getExitCode()); assertTrue( - executionResult.getErrorOutput().contains("unreachable-host: Temporary failure in name resolution") || - executionResult.getErrorOutput().contains("nodename nor servname provided, or not known")); + executionResult.getErrorOutput().contains("Unable to connect.")); } @ParameterizedTest @@ -97,8 +96,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(1, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("readAddress(..) failed: Connection reset by peer") || - executionResult.getErrorOutput().contains("Connection refused")); + assertTrue(executionResult.getErrorOutput().contains("Unable to connect.")); } @ParameterizedTest diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index ca14b99c7..316012d68 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -134,9 +134,9 @@ void test_wrongPort(final char mqttVersion) throws Exception { List.of("con", "-h", HIVE_MQ.getHost(), "-p", "22", "-V", String.valueOf(mqttVersion), "-i", "cliTest"); mqttCliShell.executeAsync(connectCommand) - .awaitStdErr("Connection refused") + .awaitStdErr("Unable to connect.") .awaitStdOut("mqtt>") - .awaitLog("Connection refused"); + .awaitLog("Unable to connect."); assertEquals(0, HIVE_MQ.getConnectPackets().size()); } @@ -156,9 +156,9 @@ void test_wrongHost(final char mqttVersion) throws Exception { "cliTest"); mqttCliShell.executeAsync(connectCommand) - .awaitStdErr("nodename nor servname provided, or not known") + .awaitStdErr("Unable to connect.") .awaitStdOut("mqtt>") - .awaitLog("nodename nor servname provided, or not known"); + .awaitLog("Unable to connect."); assertEquals(0, HIVE_MQ.getConnectPackets().size()); } From 8b25a8c9011ce534d6b83358bea080cdef2877b2 Mon Sep 17 00:00:00 2001 From: LukasHiveMQ Date: Mon, 19 Dec 2022 15:53:03 +0100 Subject: [PATCH 64/64] Fixed connect messaging tests. --- .../java/com/hivemq/cli/commands/cli/PublishCommand.java | 3 ++- .../hivemq/cli/commands/cli/publish/PublishConnectST.java | 4 ++-- .../cli/commands/cli/subscribe/SubscribeConnectST.java | 4 ++-- .../hivemq/cli/commands/shell/connect/ShellConnectST.java | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java index c4ca77431..5278071a6 100644 --- a/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java +++ b/src/main/java/com/hivemq/cli/commands/cli/PublishCommand.java @@ -84,7 +84,8 @@ public PublishCommand(final @NotNull MqttClientExecutor mqttClientExecutor) { try { client = mqttClientExecutor.connect(connectOptions, null); } catch (final Exception exception) { - LoggerUtils.logCommandError("Unable to connect", exception, debugOptions); + System.out.println("RANDOMMMM"); + LoggerUtils.logCommandError("Unable to connect this thing", exception, debugOptions); return 1; } diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java index c564f29f6..1e071dec5 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/publish/PublishConnectST.java @@ -74,7 +74,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(1, executionResult.getExitCode()); assertTrue( - executionResult.getErrorOutput().contains("Unable to connect.")); + executionResult.getErrorOutput().contains("Unable to connect")); } @ParameterizedTest @@ -96,7 +96,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(publishCommand); assertEquals(1, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Unable to connect.")); + assertTrue(executionResult.getErrorOutput().contains("Unable to connect")); } @ParameterizedTest diff --git a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java index ba0884893..9858c9448 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/cli/subscribe/SubscribeConnectST.java @@ -76,7 +76,7 @@ void test_connectWrongHost(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(1, executionResult.getExitCode()); assertTrue( - executionResult.getErrorOutput().contains("Unable to connect.")); + executionResult.getErrorOutput().contains("Unable to connect")); } @ParameterizedTest @@ -96,7 +96,7 @@ void test_connectWrongPort(final char mqttVersion) throws Exception { final ExecutionResult executionResult = MqttCli.execute(subscribeCommand); assertEquals(1, executionResult.getExitCode()); - assertTrue(executionResult.getErrorOutput().contains("Unable to connect.")); + assertTrue(executionResult.getErrorOutput().contains("Unable to connect")); } @ParameterizedTest diff --git a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java index 316012d68..bc596c727 100644 --- a/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java +++ b/src/systemTest/java/com/hivemq/cli/commands/shell/connect/ShellConnectST.java @@ -134,9 +134,9 @@ void test_wrongPort(final char mqttVersion) throws Exception { List.of("con", "-h", HIVE_MQ.getHost(), "-p", "22", "-V", String.valueOf(mqttVersion), "-i", "cliTest"); mqttCliShell.executeAsync(connectCommand) - .awaitStdErr("Unable to connect.") + .awaitStdErr("Unable to connect") .awaitStdOut("mqtt>") - .awaitLog("Unable to connect."); + .awaitLog("Unable to connect"); assertEquals(0, HIVE_MQ.getConnectPackets().size()); } @@ -156,9 +156,9 @@ void test_wrongHost(final char mqttVersion) throws Exception { "cliTest"); mqttCliShell.executeAsync(connectCommand) - .awaitStdErr("Unable to connect.") + .awaitStdErr("Unable to connect") .awaitStdOut("mqtt>") - .awaitLog("Unable to connect."); + .awaitLog("Unable to connect"); assertEquals(0, HIVE_MQ.getConnectPackets().size()); }