diff --git a/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/HubTierConnectionAndroidRunner.java b/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/HubTierConnectionAndroidRunner.java deleted file mode 100644 index 788e271b58..0000000000 --- a/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/HubTierConnectionAndroidRunner.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.microsoft.azure.sdk.iot.android.iothub; - -import com.microsoft.azure.sdk.iot.android.helper.TestGroup10; -import com.microsoft.azure.sdk.iot.device.DeviceClient; -import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol; -import com.microsoft.azure.sdk.iot.service.registry.RegistryIdentity; -import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import tests.integration.com.microsoft.azure.sdk.iot.iothub.HubTierConnectionTests; - -@TestGroup10 -@RunWith(Parameterized.class) -public class HubTierConnectionAndroidRunner extends HubTierConnectionTests -{ - public HubTierConnectionAndroidRunner(DeviceClient client, IotHubClientProtocol protocol, RegistryIdentity identity, AuthenticationType authenticationType, boolean useHttpProxy) - { - super(client, protocol, identity, authenticationType, useHttpProxy); - } -} diff --git a/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/connection/ConnectionTestsAndroidRunner.java b/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/connection/ConnectionTestsAndroidRunner.java new file mode 100644 index 0000000000..255792674a --- /dev/null +++ b/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/connection/ConnectionTestsAndroidRunner.java @@ -0,0 +1,19 @@ +package com.microsoft.azure.sdk.iot.android.iothub.connection; + +import com.microsoft.azure.sdk.iot.android.helper.TestGroup11; +import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol; +import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.ClientType; +import tests.integration.com.microsoft.azure.sdk.iot.iothub.connection.ConnectionTests; + +@TestGroup11 +@RunWith(Parameterized.class) +public class ConnectionTestsAndroidRunner extends ConnectionTests +{ + public ConnectionTestsAndroidRunner(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean withProxy) throws Exception + { + super(protocol, authenticationType, clientType, withProxy); + } +} diff --git a/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/errorinjection/SendMessagesErrInjAndroidRunner.java b/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/errorinjection/SendMessagesErrInjAndroidRunner.java index 81bd41b807..6445c718d1 100644 --- a/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/errorinjection/SendMessagesErrInjAndroidRunner.java +++ b/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/errorinjection/SendMessagesErrInjAndroidRunner.java @@ -19,8 +19,8 @@ @RunWith(Parameterized.class) public class SendMessagesErrInjAndroidRunner extends SendMessagesErrInjTests { - public SendMessagesErrInjAndroidRunner(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean useHttpProxy) throws Exception + public SendMessagesErrInjAndroidRunner(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) throws Exception { - super(protocol, authenticationType, clientType, useHttpProxy); + super(protocol, authenticationType, clientType); } } diff --git a/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/messaging/SendMessagesAndroidRunner.java b/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/messaging/SendMessagesAndroidRunner.java index 25ba52e4fa..8b553fc856 100644 --- a/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/messaging/SendMessagesAndroidRunner.java +++ b/iot-e2e-tests/android/app/src/androidTest/java/com/microsoft/azure/sdk/iot/android/iothub/messaging/SendMessagesAndroidRunner.java @@ -19,8 +19,8 @@ @RunWith(Parameterized.class) public class SendMessagesAndroidRunner extends SendMessagesTests { - public SendMessagesAndroidRunner(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean useHttpProxy) throws Exception + public SendMessagesAndroidRunner(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) throws Exception { - super(protocol, authenticationType, clientType, useHttpProxy); + super(protocol, authenticationType, clientType); } } \ No newline at end of file diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/CustomObject.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/CustomObject.java similarity index 94% rename from iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/CustomObject.java rename to iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/CustomObject.java index 2b9470a120..eb2a4e83ae 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/CustomObject.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/CustomObject.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package tests.integration.com.microsoft.azure.sdk.iot.iothub.setup; +package tests.integration.com.microsoft.azure.sdk.iot.helpers; import lombok.Getter; import lombok.Setter; diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/IotHubServicesCommon.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/IotHubServicesCommon.java index 7e1fdf597f..fcf5856faa 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/IotHubServicesCommon.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/IotHubServicesCommon.java @@ -22,246 +22,9 @@ */ public class IotHubServicesCommon { - //if error injection message has not taken effect after 1 minute, the test will timeout - private final static long ERROR_INJECTION_MESSAGE_EFFECT_TIMEOUT_MILLISECONDS = 60 * 1000; - private final static String TEST_ASC_SECURITY_MESSAGE = "{ \"AgentVersion\": \"0.0.1\", " - + "\"AgentId\" : \"{4C1B4747-E4C7-4681-B31D-4B39E390E7F8}\", " - + "\"MessageSchemaVersion\" : \"1.0\", \"Events\" : " - + " { \"EventType\": \"Security\", " - + "\"Category\" : \"Periodic\", " - + "\"Name\" : \"ListeningPorts\", " - + "\"IsEmpty\" : true, " - + "\"PayloadSchemaVersion\" : \"1.0\", " - + "\"Id\" : \"%s\", " - + "\"TimestampLocal\" : \"2012-04-23T18:25:43.511Z\", " - + "\"TimestampUTC\" : \"2012-04-23T18:25:43.511Z\" }, " - + "\"Payload\": { \"data\": \"test\" } } }"; - private static final int TIMEOUT_MILLISECONDS = 60 * 1000; // 1 minute private static final int CHECK_INTERVAL_MILLISECONDS = 300; - /* - * method to send message over given DeviceClient - */ - public static void sendMessages(InternalClient client, - IotHubClientProtocol protocol, - List messagesToSend, - final long RETRY_MILLISECONDS, - final long SEND_TIMEOUT_MILLISECONDS, - long interMessageDelay, - List> statusUpdates) throws IOException, InterruptedException, IotHubClientException - { - try - { - client.open(false); - - for (MessageAndResult messageAndResult : messagesToSend) - { - if ((protocol == IotHubClientProtocol.MQTT || protocol == IotHubClientProtocol.MQTT_WS) && isErrorInjectionMessage(messageAndResult)) - { - // error injection message will not be ack'd by service if sent over MQTT/MQTT_WS, so the SDK's - // retry logic will try to send it again after the connection drops. By setting expiry time, - // we ensure that error injection message isn't resent to service too many times. The message will still likely - // be sent 3 or 4 times causing 3 or 4 disconnections, but the test should recover anyways. - messageAndResult.message.setExpiryTime(1000); - - // Since the message won't be ack'd, then we don't need to validate the status code when this message's callback is fired - messageAndResult.statusCode = null; - } - - sendMessageAndWaitForResponse(client, messageAndResult, protocol); - - if (isErrorInjectionMessage(messageAndResult)) - { - //wait until error injection message takes affect before sending the next message - long startTime = System.currentTimeMillis(); - while (!actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED_RETRYING)) - { - Thread.sleep(1000); - - // send the fault injection message again in case it wasn't sent successfully before - sendMessageAndWaitForResponse(client, messageAndResult, protocol); - - if (System.currentTimeMillis() - startTime > ERROR_INJECTION_MESSAGE_EFFECT_TIMEOUT_MILLISECONDS) - { - Assert.fail(buildExceptionMessage("Sending message over " + protocol + " protocol failed: Error injection message never caused connection to be lost", client)); - } - } - } - else - { - Thread.sleep(interMessageDelay); - } - } - } - finally - { - client.close(); - } - } - - /* - * method to send message over given DeviceClient - */ - public static void sendBulkMessages(InternalClient client, - IotHubClientProtocol protocol, - List messagesToSend, - final long RETRY_MILLISECONDS, - final long SEND_TIMEOUT_MILLISECONDS, - long interMessageDelay, - List> statusUpdates) throws IOException, InterruptedException, IotHubClientException - { - try - { - client.open(false); - - if (protocol != IotHubClientProtocol.HTTPS) - { - sendMessages(client, protocol, messagesToSend,RETRY_MILLISECONDS ,SEND_TIMEOUT_MILLISECONDS,interMessageDelay, statusUpdates); - return; - } - - List bulkMessages = new ArrayList<>(); - for (MessageAndResult mar : messagesToSend) { - bulkMessages.add(mar.message); - } - - BulkMessagesAndResult bulkMessagesAndResult = new BulkMessagesAndResult(bulkMessages, IotHubStatusCode.OK); - sendBulkMessagesAndWaitForResponse(client, bulkMessagesAndResult, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, protocol); - } - finally - { - client.close(); - } - } - - public static void sendMessagesExpectingConnectionStatusChangeUpdate(InternalClient client, - IotHubClientProtocol protocol, - List messagesToSend, - final long RETRY_MILLISECONDS, - final long SEND_TIMEOUT_MILLISECONDS, - final IotHubConnectionStatus expectedStatus, - int interMessageDelay, - AuthenticationType authType) throws IOException, InterruptedException, IotHubClientException - { - final List> actualStatusUpdates = new ArrayList<>(); - client.setConnectionStatusChangeCallback((context) -> actualStatusUpdates.add(new Pair<>(context.getNewStatus(), context.getCause())), new Object()); - - sendMessages(client, protocol, messagesToSend, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, interMessageDelay, actualStatusUpdates); - - Assert.assertTrue(buildExceptionMessage(protocol + ", " + authType + ": Expected connection status update to occur: " + expectedStatus, client), actualStatusUpdatesContainsStatus(actualStatusUpdates, expectedStatus)); - } - - /* - * method to send message over given DeviceClient - */ - public static void sendMessagesMultiplex(InternalClient client, - IotHubClientProtocol protocol, - final int NUM_MESSAGES_PER_CONNECTION, - final long RETRY_MILLISECONDS, - final long SEND_TIMEOUT_MILLISECONDS) - { - String messageString = "Java client e2e test message over " + protocol + " protocol"; - Message msg = new Message(messageString); - - for (int i = 0; i < NUM_MESSAGES_PER_CONNECTION; ++i) - { - try - { - Success messageSent = new Success(); - EventCallback callback = new EventCallback(IotHubStatusCode.OK); - client.sendEventAsync(msg, callback, messageSent); - - long startTime = System.currentTimeMillis(); - while (!messageSent.wasCallbackFired()) - { - Thread.sleep(RETRY_MILLISECONDS); - if (System.currentTimeMillis() - startTime > SEND_TIMEOUT_MILLISECONDS) - { - Assert.fail(buildExceptionMessage("Timed out waiting for message callback", client)); - } - } - - if (messageSent.getCallbackStatusCode() != IotHubStatusCode.OK) - { - Assert.fail(buildExceptionMessage("Sending message over " + protocol + " protocol failed: expected status code OK but received: " + messageSent.getCallbackStatusCode(), client)); - } - } - catch (Exception e) - { - Assert.fail(buildExceptionMessage("Sending message over " + protocol + " protocol failed: Exception encountered while sending messages: " + e.getMessage(), client)); - } - } - } - - public static void sendExpiredMessageExpectingMessageExpiredCallback(InternalClient client, - IotHubClientProtocol protocol, - final long RETRY_MILLISECONDS, - final long SEND_TIMEOUT_MILLISECONDS, - AuthenticationType authType) throws IOException - { - try - { - Message expiredMessage = new Message("This message has expired"); - expiredMessage.setAbsoluteExpiryTime(1); //setting this to 0 causes the message to never expire - Success messageSentExpiredCallback = new Success(); - - client.open(false); - client.sendEventAsync(expiredMessage, new EventCallback(IotHubStatusCode.MESSAGE_EXPIRED), messageSentExpiredCallback); - - long startTime = System.currentTimeMillis(); - while (!messageSentExpiredCallback.wasCallbackFired()) - { - Thread.sleep(RETRY_MILLISECONDS); - if (System.currentTimeMillis() - startTime > SEND_TIMEOUT_MILLISECONDS) - { - Assert.fail(buildExceptionMessage(protocol + ", " + authType + ": Timed out waiting for a message callback", client)); - } - } - - client.close(); - - if (messageSentExpiredCallback.getCallbackStatusCode() != IotHubStatusCode.MESSAGE_EXPIRED) - { - Assert.fail(buildExceptionMessage("Sending message over " + protocol + " protocol failed: expected status code MESSAGE_EXPIRED but received: " + messageSentExpiredCallback.getCallbackStatusCode(), client)); - } - } - catch (Exception e) - { - client.close(); - Assert.fail(buildExceptionMessage("Sending expired message over " + protocol + " protocol failed: Exception encountered while sending message and waiting for MESSAGE_EXPIRED callback: " + e.getMessage(), client)); - } - } - - public static void sendMessagesExpectingUnrecoverableConnectionLossAndTimeout(InternalClient client, - IotHubClientProtocol protocol, - Message errorInjectionMessage, - AuthenticationType authType) throws IOException, InterruptedException, IotHubClientException - { - final List> statusUpdates = new ArrayList<>(); - client.setConnectionStatusChangeCallback((context) -> statusUpdates.add(new Pair<>(context.getNewStatus(), context.getCause())), new Object()); - - client.open(false); - - client.sendEventAsync(errorInjectionMessage, new EventCallback(null), new Success()); - - long startTime = System.currentTimeMillis(); - while (!(actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED_RETRYING) && actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED))) - { - Thread.sleep(500); - - if (System.currentTimeMillis() - startTime > 30 * 1000) - { - break; - } - } - - Assert.assertTrue(buildExceptionMessage(protocol + ", " + authType + ": Expected notification about disconnected but retrying.", client), actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED_RETRYING)); - Assert.assertTrue(buildExceptionMessage(protocol + ", " + authType + ": Expected notification about disconnected.", client), actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED)); - - client.close(); - } - public static void sendErrorInjectionMessageAndWaitForResponse(InternalClient client, MessageAndResult messageAndResult, IotHubClientProtocol protocol) { if (protocol == IotHubClientProtocol.MQTT || protocol == IotHubClientProtocol.MQTT_WS) @@ -309,50 +72,6 @@ public static void sendMessageAndWaitForResponse(InternalClient client, MessageA } } - public static void sendBulkMessagesAndWaitForResponse(InternalClient client, BulkMessagesAndResult messagesAndResults, long RETRY_MILLISECONDS, long SEND_TIMEOUT_MILLISECONDS, IotHubClientProtocol protocol) - { - try - { - Success messageSent = new Success(); - EventCallback callback = new EventCallback(messagesAndResults.statusCode); - client.sendEventsAsync(messagesAndResults.messages, callback, messageSent); - - long startTime = System.currentTimeMillis(); - while (!messageSent.wasCallbackFired()) - { - Thread.sleep(RETRY_MILLISECONDS); - if (System.currentTimeMillis() - startTime > SEND_TIMEOUT_MILLISECONDS) - { - Assert.fail(buildExceptionMessage("Timed out waiting for a message callback", client)); - break; - } - } - - if (messagesAndResults.statusCode != null && messageSent.getCallbackStatusCode() != messagesAndResults.statusCode) - { - Assert.fail(buildExceptionMessage("Sending message over " + protocol + " protocol failed: expected " + messagesAndResults.statusCode + " but received " + messageSent.getCallbackStatusCode(), client)); - } - } - catch (Exception e) - { - Assert.fail(buildExceptionMessage("Sending message over " + protocol + " protocol failed: Exception encountered while sending and waiting on a message: " + e.getMessage(), client)); - } - } - - private static boolean isErrorInjectionMessage(MessageAndResult messageAndResult) - { - MessageProperty[] properties = messageAndResult.message.getProperties(); - for (MessageProperty property : properties) - { - if (property.getValue().equals(ErrorInjectionHelper.FaultCloseReason_Boom) || property.getValue().equals(ErrorInjectionHelper.FaultCloseReason_Bye)) - { - return true; - } - } - - return false; - } - public static void waitForStabilizedConnection(List> actualStatusUpdates, InternalClient client) throws InterruptedException { //Wait until error injection takes effect diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/NestedCustomObject.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/NestedCustomObject.java similarity index 91% rename from iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/NestedCustomObject.java rename to iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/NestedCustomObject.java index a1155c2620..ee451545ff 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/NestedCustomObject.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/NestedCustomObject.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package tests.integration.com.microsoft.azure.sdk.iot.iothub.setup; +package tests.integration.com.microsoft.azure.sdk.iot.helpers; import lombok.Getter; diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/Tools.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/Tools.java index 6f26f0cc14..cd4e229c44 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/Tools.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/helpers/Tools.java @@ -256,7 +256,7 @@ private static TestDeviceIdentity getSasTestDevice(String iotHubConnectionString { // Don't want multiple methods calling this simultaneously and each thinking that they need to create // 100 devices. Forcing them to enter this block one at a time means that the first caller creates the 100 devices, - // and the subsequent callers just used one of those devices + // and the subsequent callers just uses one of those devices. synchronized (testSasDeviceQueueLock) { TestDeviceIdentity testDeviceIdentity; @@ -317,7 +317,7 @@ private static TestDeviceIdentity getX509TestDevice(String iotHubConnectionStrin { // Don't want multiple methods calling this simultaneously and each thinking that they need to create // 100 devices. Forcing them to enter this block one at a time means that the first caller creates the 100 devices, - // and the subsequent callers just used one of those devices + // and the subsequent callers just uses one of those devices. synchronized (testX509DeviceQueueLock) { TestDeviceIdentity testDeviceIdentity; @@ -964,7 +964,7 @@ public static String getStackTraceFromThrowable(Throwable throwable) public static boolean isLinux() { - return System.getProperty("os.name").toLowerCase().contains("linux"); + return !isAndroid() && System.getProperty("os.name").toLowerCase().contains("linux"); } public static boolean isAndroid() diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/HubTierConnectionTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/HubTierConnectionTests.java deleted file mode 100644 index 998a252ce0..0000000000 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/HubTierConnectionTests.java +++ /dev/null @@ -1,236 +0,0 @@ -package tests.integration.com.microsoft.azure.sdk.iot.iothub; - - -import com.microsoft.azure.sdk.iot.device.*; -import com.microsoft.azure.sdk.iot.device.twin.DirectMethodPayload; -import com.microsoft.azure.sdk.iot.device.exceptions.IotHubClientException; -import com.microsoft.azure.sdk.iot.device.twin.DirectMethodResponse; -import com.microsoft.azure.sdk.iot.device.twin.Pair; -import com.microsoft.azure.sdk.iot.device.transport.IotHubConnectionStatus; -import com.microsoft.azure.sdk.iot.device.twin.SubscriptionAcknowledgedCallback; -import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; -import com.microsoft.azure.sdk.iot.service.auth.IotHubConnectionStringBuilder; -import com.microsoft.azure.sdk.iot.service.registry.RegistryClient; -import com.microsoft.azure.sdk.iot.service.registry.RegistryClientOptions; -import com.microsoft.azure.sdk.iot.service.registry.RegistryIdentity; -import com.microsoft.azure.sdk.iot.service.registry.Device; -import org.junit.*; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.proxy.HttpProxyServer; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.proxy.impl.DefaultHttpProxyServer; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.*; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.BasicTierHubOnlyTest; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.IotHubTest; - -import javax.net.ssl.SSLContext; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.util.*; - -import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.AMQPS; -import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.AMQPS_WS; -import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; -import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SELF_SIGNED; -import static tests.integration.com.microsoft.azure.sdk.iot.helpers.CorrelationDetailsLoggingAssert.buildExceptionMessage; - -@IotHubTest -@RunWith(Parameterized.class) -public class HubTierConnectionTests extends IntegrationTest -{ - protected static final long WAIT_FOR_DISCONNECT_TIMEOUT_MILLISECONDS = 60 * 1000; // 1 minute - - // How much to wait until a message makes it to the server, in milliseconds - protected static final Integer SEND_TIMEOUT_MILLISECONDS = 180000; - - //How many milliseconds between retry - protected static final Integer RETRY_MILLISECONDS = 100; - - protected static String iotHubConnectionString = ""; - protected static HttpProxyServer proxyServer; - protected static String testProxyHostname = "127.0.0.1"; - protected static int testProxyPort = 8897; - - // Semmle flags this as a security issue, but this is a test username so the warning can be suppressed - protected static final String testProxyUser = "proxyUsername"; // lgtm - - // Semmle flags this as a security issue, but this is a test password so the warning can be suppressed - protected static final char[] testProxyPass = "1234".toCharArray(); // lgtm - - protected static String hostName; - - public HubTierConnectionTestInstance testInstance; - - protected static RegistryClient registryClient; - - public HubTierConnectionTests(DeviceClient client, IotHubClientProtocol protocol, RegistryIdentity identity, AuthenticationType authenticationType, boolean useHttpProxy) - { - this.testInstance = new HubTierConnectionTestInstance(client, protocol, identity, authenticationType, useHttpProxy); - } - - @Parameterized.Parameters(name = "{1}_{3}_{4}") - public static Collection inputs() throws Exception - { - iotHubConnectionString = Tools.retrieveEnvironmentVariableValue(TestConstants.IOT_HUB_CONNECTION_STRING_ENV_VAR_NAME); - isBasicTierHub = Boolean.parseBoolean(Tools.retrieveEnvironmentVariableValue(TestConstants.IS_BASIC_TIER_HUB_ENV_VAR_NAME)); - isPullRequest = Boolean.parseBoolean(Tools.retrieveEnvironmentVariableValue(TestConstants.IS_PULL_REQUEST)); - - String x509Thumbprint = x509CertificateGenerator.getX509Thumbprint(); - - registryClient = new RegistryClient(iotHubConnectionString, RegistryClientOptions.builder().httpReadTimeoutSeconds(HTTP_READ_TIMEOUT).build()); - String uuid = UUID.randomUUID().toString(); - String deviceId = "java-tier-connection-e2e-test".concat("-" + uuid); - String deviceIdX509 = "java-tier-connection-e2e-test-X509".concat("-" + uuid); - - Device device = new Device(deviceId); - Device deviceX509 = new Device(deviceIdX509, SELF_SIGNED); - - deviceX509.setThumbprint(x509Thumbprint, x509Thumbprint); - - Tools.addDeviceWithRetry(registryClient, device); - Tools.addDeviceWithRetry(registryClient, deviceX509); - - hostName = IotHubConnectionStringBuilder.createIotHubConnectionString(iotHubConnectionString).getHostName(); - SSLContext sslContext = SSLContextBuilder.buildSSLContext(x509CertificateGenerator.getX509Certificate(), x509CertificateGenerator.getPrivateKey()); - - ClientOptions x509ClientOptions = ClientOptions.builder().sslContext(sslContext).build(); - - Proxy testProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(testProxyHostname, testProxyPort)); - - return new ArrayList(Arrays.asList( - new Object[][] - { - //sas token device client - {new DeviceClient(DeviceConnectionString.get(iotHubConnectionString, device), AMQPS), AMQPS, device, SAS, false}, - {new DeviceClient(DeviceConnectionString.get(iotHubConnectionString, device), AMQPS_WS), AMQPS_WS, device, SAS, false}, - - //x509 device client - {new DeviceClient(DeviceConnectionString.get(iotHubConnectionString, deviceX509), AMQPS, x509ClientOptions), AMQPS, deviceX509, SELF_SIGNED, false}, - - //sas token device client, with proxy - {new DeviceClient(DeviceConnectionString.get(iotHubConnectionString, device), AMQPS_WS, x509ClientOptions), AMQPS_WS, device, SAS, true} - } - )); - } - - @BeforeClass - public static void classSetup() - { - registryClient = new RegistryClient(iotHubConnectionString, RegistryClientOptions.builder().httpReadTimeoutSeconds(HTTP_READ_TIMEOUT).build()); - } - - @BeforeClass - public static void startProxy() - { - proxyServer = DefaultHttpProxyServer.bootstrap() - .withPort(testProxyPort) - .withProxyAuthenticator(new BasicProxyAuthenticator(testProxyUser, testProxyPass)) - .start(); - } - - @AfterClass - public static void stopProxy() - { - proxyServer.stop(); - } - - @Before - public void SetProxyIfApplicable() - { - - } - - public static class HubTierConnectionTestInstance - { - public DeviceClient client; - public IotHubClientProtocol protocol; - public RegistryIdentity identity; - public AuthenticationType authenticationType; - public ClientType clientType; - public String publicKeyCert; - public String privateKey; - public String x509Thumbprint; - public CorrelationDetailsLoggingAssert correlationDetailsLoggingAssert; - public boolean useHttpProxy; - - public HubTierConnectionTestInstance(DeviceClient client, IotHubClientProtocol protocol, RegistryIdentity identity, AuthenticationType authenticationType, boolean useHttpProxy) - { - this.client = client; - this.protocol = protocol; - this.identity = identity; - this.authenticationType = authenticationType; - this.publicKeyCert = x509CertificateGenerator.getPublicCertificatePEM(); - this.privateKey = x509CertificateGenerator.getPrivateKeyPEM(); - this.x509Thumbprint = x509CertificateGenerator.getX509Thumbprint(); - String deviceId = identity.getDeviceId(); - this.useHttpProxy = useHttpProxy; - - this.correlationDetailsLoggingAssert = new CorrelationDetailsLoggingAssert(this.client.getConfig().getIotHubHostname(), deviceId, protocol.toString(), null); - } - } - - protected static class MethodCallback implements com.microsoft.azure.sdk.iot.device.twin.MethodCallback - { - @Override - public DirectMethodResponse onMethodInvoked(String methodName, DirectMethodPayload methodData, Object context) - { - return new DirectMethodResponse(200, "payload"); - } - } - - protected static class DirectMethodStatusCallback implements SubscriptionAcknowledgedCallback - { - public void onSubscriptionAcknowledged(IotHubClientException e, Object context) - { - IotHubStatusCode status = e == null ? IotHubStatusCode.OK : e.getStatusCode(); - System.out.println("Device Client: IoT Hub responded to device method operation with status " + status.name()); - } - } - - @Test - @BasicTierHubOnlyTest - public void enableMethodFailedWithBasicTier() throws IOException, InterruptedException, IotHubClientException - { - //arrange - List> connectionStatusUpdates = new ArrayList<>(); - testInstance.client.setConnectionStatusChangeCallback((context) -> connectionStatusUpdates.add(new Pair<>(context.getNewStatus(), context.getCause())), null); - - testInstance.client.open(false); - - //act - testInstance.client.subscribeToMethodsAsync(new MethodCallback(), null, new DirectMethodStatusCallback(), null); - - //assert - waitForDisconnect(connectionStatusUpdates, WAIT_FOR_DISCONNECT_TIMEOUT_MILLISECONDS, testInstance.client); - testInstance.client.close(); - } - - public static void waitForDisconnect(List> actualStatusUpdates, long timeout, InternalClient client) throws InterruptedException - { - //Wait for DISCONNECTED - long startTime = System.currentTimeMillis(); - while (!actualStatusUpdatesContainsStatus(actualStatusUpdates, IotHubConnectionStatus.DISCONNECTED)) - { - Thread.sleep(2000); - long timeElapsed = System.currentTimeMillis() - startTime; - if (timeElapsed > timeout) - { - Assert.fail(buildExceptionMessage("Timed out waiting for disconnection to take effect", client)); - } - } - } - - public static boolean actualStatusUpdatesContainsStatus(List> actualStatusUpdates, IotHubConnectionStatus status) - { - for (Pair actualStatusUpdate : actualStatusUpdates) - { - if (actualStatusUpdate.getKey() == status) - { - return true; - } - } - - return false; - } -} diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/MultiplexingClientTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/MultiplexingClientTests.java index 0b04682562..f38275db06 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/MultiplexingClientTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/MultiplexingClientTests.java @@ -81,6 +81,7 @@ import java.util.concurrent.atomic.AtomicReference; import static junit.framework.TestCase.*; +import static org.junit.Assume.assumeTrue; import static tests.integration.com.microsoft.azure.sdk.iot.iothub.setup.TwinCommon.isPropertyInTwinCollection; /** @@ -132,7 +133,6 @@ public static Collection inputs() throws Exception return Arrays.asList( new Object[][] { - {IotHubClientProtocol.AMQPS}, {IotHubClientProtocol.AMQPS_WS} }); } @@ -228,27 +228,11 @@ public static void stopProxy() proxyServer.stop(); } - @Test - public void openClientWithRetry() throws Exception - { - testInstance.setup(DEVICE_MULTIPLEX_COUNT); - testInstance.multiplexingClient.open(true); - testInstance.multiplexingClient.close(); - } - - @Test - public void openClientWithRetryWithoutRegisteredDevices() throws Exception - { - testInstance.setup(0); - testInstance.multiplexingClient.open(true); - testInstance.multiplexingClient.close(); - } - @Test public void sendMessages() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); testSendingMessagesFromMultiplexedClients(testInstance.deviceClientArray); @@ -256,6 +240,7 @@ public void sendMessages() throws Exception } @Test + @ContinuousIntegrationTest public void connectionStatusCallbackExecutedWithNoDevices() throws Exception { // Even with no devices registered to a multiplexed connection, the connection status callback should onStatusChanged @@ -263,7 +248,7 @@ public void connectionStatusCallbackExecutedWithNoDevices() throws Exception testInstance.setup(0); ConnectionStatusChangeTracker connectionStatusChangeTracker = new ConnectionStatusChangeTracker(); testInstance.multiplexingClient.setConnectionStatusChangeCallback(connectionStatusChangeTracker, null); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); assertTrue( "Connection status callback never executed with CONNECTED status after opening multiplexing client with no devices registered", @@ -279,11 +264,12 @@ public void connectionStatusCallbackExecutedWithNoDevices() throws Exception // MultiplexingClient should be able to open an AMQP connection to IoTHub with no device sessions, and should // allow for device sessions to be added and used later. @Test + @ContinuousIntegrationTest public void openMultiplexingClientWithoutAnyRegisteredDevices() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); testInstance.multiplexingClient.unregisterDeviceClients(testInstance.deviceClientArray); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); testInstance.multiplexingClient.registerDeviceClients(testInstance.deviceClientArray); @@ -293,10 +279,11 @@ public void openMultiplexingClientWithoutAnyRegisteredDevices() throws Exception } @Test + @ContinuousIntegrationTest public void canUnregisterAllClientsThenReregisterAllClientsOnOpenConnection() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); testInstance.multiplexingClient.unregisterDeviceClients(testInstance.deviceClientArray); testInstance.multiplexingClient.registerDeviceClients(testInstance.deviceClientArray); @@ -313,11 +300,11 @@ public void canReopenClosedMultiplexingClient() throws Exception testInstance.setup(DEVICE_MULTIPLEX_COUNT); // Open and close the connection once - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); testInstance.multiplexingClient.close(); // Re-open the connection and verify that it can still send telemetry - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); testSendingMessagesFromMultiplexedClients(testInstance.deviceClientArray); testInstance.multiplexingClient.close(); } @@ -338,7 +325,7 @@ public void sendMessagesMaxDevicesAllowed() throws Exception testInstance.setup(MultiplexingClient.MAX_MULTIPLEX_DEVICE_COUNT_AMQPS_WS); } - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); testSendingMessagesFromMultiplexedClients(testInstance.deviceClientArray); @@ -378,7 +365,7 @@ public void sendMessagesMaxDevicesAllowedTimes10MultiplexingClientsParallelOpen( try { openExceptions[finalI] = new AtomicReference<>(); - testInstances[finalI].multiplexingClient.open(false); + testInstances[finalI].multiplexingClient.open(true); } catch (IotHubClientException e) { @@ -453,7 +440,7 @@ public void sendMessagesMaxDevicesAllowedTimes10MultiplexingClientsSerialOpen() long startOpenTime = System.currentTimeMillis(); for (int i = 0; i < multiplexingClientCount; i++) { - testInstances[i].multiplexingClient.open(false); + testInstances[i].multiplexingClient.open(true); } long finishOpenTime = System.currentTimeMillis(); @@ -484,18 +471,15 @@ public void sendMessagesMaxDevicesAllowedTimes10MultiplexingClientsSerialOpen() @Test public void sendMessagesWithProxy() throws Exception { - if (testInstance.protocol != IotHubClientProtocol.AMQPS_WS) - { - // only AMQPS_WS supports proxies - return; - } + // only AMQPS_WS supports proxies + assumeTrue(testInstance.protocol == IotHubClientProtocol.AMQPS_WS); Proxy testProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(testProxyHostname, testProxyPort)); ProxySettings proxySettings = new ProxySettings(testProxy, testProxyUser, testProxyPass); //re-setup test instance to use proxy instead testInstance.setup(DEVICE_MULTIPLEX_COUNT, MultiplexingClientOptions.builder().proxySettings(proxySettings).build(), false); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); testSendingMessagesFromMultiplexedClients(testInstance.deviceClientArray); @@ -555,7 +539,7 @@ private static void waitForMessageToBeAcknowledged(Success messageSendSuccess, S public void receiveMessagesIncludingProperties() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); for (int i = 0; i < DEVICE_MULTIPLEX_COUNT; i++) { @@ -574,10 +558,11 @@ public void receiveMessagesIncludingProperties() throws Exception // MessageCallback for cloud to device messages should not be preserved between registrations by default @Test @StandardTierHubOnlyTest + @ContinuousIntegrationTest public void cloudToDeviceMessageSubscriptionNotPreservedByDeviceClientAfterUnregistration() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); for (int i = 0; i < DEVICE_MULTIPLEX_COUNT; i++) { String expectedMessageCorrelationId = UUID.randomUUID().toString(); @@ -661,7 +646,7 @@ public void resetExpectations(String newExpectedMessageCorrelationId) public void invokeMethodSucceed() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); DirectMethodsClient directMethodServiceClientClient = new DirectMethodsClient(iotHubConnectionString); for (int i = 0; i < DEVICE_MULTIPLEX_COUNT; i++) @@ -683,7 +668,7 @@ public void invokeMethodSucceed() throws Exception public void methodsSubscriptionNotPreservedByDeviceClientAfterUnregistration() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); DirectMethodsClient directMethodServiceClientClient = new DirectMethodsClient(iotHubConnectionString); List directDirectMethodCallbacks = new ArrayList<>(); List expectedMethodNames = new ArrayList<>(); @@ -764,13 +749,12 @@ public void resetExpectations() } } - @Test @StandardTierHubOnlyTest public void testTwin() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT, MultiplexingClientOptions.builder().build(), true); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); TwinClient twinClientServiceClient = new TwinClient(iotHubConnectionString, TwinClientOptions.builder().httpReadTimeoutSeconds(0).build()); CountDownLatch[] desiredPropertyUpdateLatches = new CountDownLatch[DEVICE_MULTIPLEX_COUNT]; @@ -814,7 +798,7 @@ public void testTwin() throws Exception public void twinSubscriptionNotPreservedByDeviceClientAfterUnregistration() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT, MultiplexingClientOptions.builder().build(), true); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); TwinClient twinClientServiceClient = new TwinClient(iotHubConnectionString, TwinClientOptions.builder().httpReadTimeoutSeconds(0).build()); final String expectedPropertyKey = UUID.randomUUID().toString(); @@ -990,6 +974,7 @@ else if (status == IotHubConnectionStatus.DISCONNECTED_RETRYING) // can still be used to send telemetry. @Test @StandardTierHubOnlyTest + @ContinuousIntegrationTest public void registerClientAfterOpen() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); @@ -998,7 +983,7 @@ public void registerClientAfterOpen() throws Exception DeviceClient clientToRegisterAfterOpen = testInstance.deviceClientArray.get(DEVICE_MULTIPLEX_COUNT - 1); testInstance.multiplexingClient.unregisterDeviceClient(clientToRegisterAfterOpen); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); ConnectionStatusChangeTracker connectionStatusChangeTracker = new ConnectionStatusChangeTracker(); clientToRegisterAfterOpen.setConnectionStatusChangeCallback(connectionStatusChangeTracker, null); @@ -1014,6 +999,7 @@ public void registerClientAfterOpen() throws Exception // can still be used to send telemetry. @Test @StandardTierHubOnlyTest + @ContinuousIntegrationTest public void unregisterClientAfterOpen() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); @@ -1024,7 +1010,7 @@ public void unregisterClientAfterOpen() throws Exception ConnectionStatusChangeTracker connectionStatusChangeTracker = new ConnectionStatusChangeTracker(); clientToUnregisterAfterOpen.setConnectionStatusChangeCallback(connectionStatusChangeTracker, null); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); assertConnectionStateCallbackFiredConnected(connectionStatusChangeTracker, DEVICE_SESSION_OPEN_TIMEOUT); @@ -1058,6 +1044,7 @@ public void unregisterClientAfterOpen() throws Exception @Test @ErrInjTest @IotHubTest + @ContinuousIntegrationTest public void multiplexedConnectionRecoversFromDeviceSessionDropsSequential() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); @@ -1071,7 +1058,7 @@ public void multiplexedConnectionRecoversFromDeviceSessionDropsSequential() thro testInstance.deviceClientArray.get(i).setConnectionStatusChangeCallback(connectionStatusChangeTrackers[i], testInstance.deviceClientArray.get(i).getConfig().getDeviceId()); } - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); for (int i = 0; i < DEVICE_MULTIPLEX_COUNT; i++) { @@ -1117,6 +1104,7 @@ public void multiplexedConnectionRecoversFromDeviceSessionDropsSequential() thro @Test @ErrInjTest @IotHubTest + @ContinuousIntegrationTest public void multiplexedConnectionRecoversFromDeviceSessionDropsParallel() throws Exception { testInstance.setup(DEVICE_MULTIPLEX_COUNT); @@ -1128,7 +1116,7 @@ public void multiplexedConnectionRecoversFromDeviceSessionDropsParallel() throws testInstance.deviceClientArray.get(i).setConnectionStatusChangeCallback(connectionStatusChangeTrackers[i], testInstance.deviceClientArray.get(i).getConfig().getDeviceId()); } - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); for (int i = 0; i < DEVICE_MULTIPLEX_COUNT; i++) { @@ -1189,7 +1177,7 @@ public void multiplexedConnectionRecoversFromTcpConnectionDrop() throws Exceptio testInstance.multiplexingClient.setConnectionStatusChangeCallback(multiplexedConnectionStatusChangeTracker, null); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); assertTrue( "Multiplexed level connection status callback should have fired with CONNECTED after opening the multiplexing client", @@ -1335,7 +1323,7 @@ public void registerDeviceWithIncorrectCredentialsAfterOpenThrows() throws Excep testInstance.multiplexingClient.unregisterDeviceClient(testInstance.deviceClientArray.get(0)); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); // Get a valid connection string, but swap out the deviceId for a deviceId that does exist, but whose symmetric key is different String incorrectConnectionString = Tools.getDeviceConnectionString(iotHubConnectionString, testInstance.deviceIdentityArray.get(1)).replace(testInstance.deviceIdentityArray.get(1).getDeviceId(), testInstance.deviceIdentityArray.get(0).getDeviceId()); @@ -1381,7 +1369,7 @@ public void registerDeviceWithIncorrectCredentialsBeforeOpenThrowsOnOpen() throw boolean expectedExceptionThrown = false; try { - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); } catch (MultiplexingClientRegistrationException e) { @@ -1411,7 +1399,7 @@ public void registerDevicesWithIncorrectCredentialsAfterOpenThrows() throws Exce testInstance.multiplexingClient.unregisterDeviceClient(testInstance.deviceClientArray.get(i)); } - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); List clientsWithIncorrectCredentials = new ArrayList<>(); for (int i = 0; i < DEVICE_MULTIPLEX_COUNT; i++) @@ -1491,7 +1479,7 @@ public void registerDevicesWithIncorrectCredentialsBeforeOpenThrowsOnOpen() thro boolean expectedExceptionThrown = false; try { - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); } catch (MultiplexingClientRegistrationException e) { @@ -1513,6 +1501,7 @@ public void registerDevicesWithIncorrectCredentialsBeforeOpenThrowsOnOpen() thro } @Test + @ContinuousIntegrationTest public void registrationsUnwindForMqttClient() throws Exception { Device mqttDevice = Tools.getTestDevice(iotHubConnectionString, IotHubClientProtocol.MQTT, AuthenticationType.SAS, false).getDevice(); @@ -1524,6 +1513,7 @@ public void registrationsUnwindForMqttClient() throws Exception } @Test + @ContinuousIntegrationTest public void registrationsUnwindForX509Client() throws Exception { // Create a new device client that uses x509 auth, which should throw an UnsupportedOperationException @@ -1536,6 +1526,7 @@ public void registrationsUnwindForX509Client() throws Exception } @Test + @ContinuousIntegrationTest public void registrationsUnwindForAlreadyOpenClient() throws Exception { Device nonMultiplexedDevice = Tools.getTestDevice(iotHubConnectionString, testInstance.protocol, AuthenticationType.SAS, false).getDevice(); @@ -1543,12 +1534,13 @@ public void registrationsUnwindForAlreadyOpenClient() throws Exception DeviceClient nonMultiplexedDeviceClient = new DeviceClient(deviceConnectionString, testInstance.protocol); //By opening the client once, this client can no longer be registered to a multiplexing client - nonMultiplexedDeviceClient.open(false); + nonMultiplexedDeviceClient.open(true); registrationsUnwindForUnsupportedOperationExceptions(nonMultiplexedDeviceClient); nonMultiplexedDeviceClient.close(); } @Test + @ContinuousIntegrationTest public void registrationsUnwindForClientOfDifferentHostName() throws Exception { Device nonMultiplexedDevice = Tools.getTestDevice(iotHubConnectionString, testInstance.protocol, AuthenticationType.SAS, false).getDevice(); @@ -1567,6 +1559,7 @@ public void registrationsUnwindForClientOfDifferentHostName() throws Exception } @Test + @ContinuousIntegrationTest public void registrationsUnwindForDifferentProtocolClient() throws Exception { // Protocol for the new client is AMQPS if the test parameters are for AMQPS_WS, and vice versa. MultiplexingClient @@ -1598,7 +1591,7 @@ public void disableDeviceAfterOpenAndAfterRegistration() throws Exception testInstance.deviceClientArray.get(i).setConnectionStatusChangeCallback(connectionStatusChangeTrackers[i], testInstance.deviceClientArray.get(i).getConfig().getDeviceId()); } - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); // Disable a device that is on the multiplexed connection and that already has an open session Device deviceToDisable = registryClient.getDevice(testInstance.deviceIdentityArray.get(0).getDeviceId()); @@ -1661,7 +1654,7 @@ public void disableDeviceAfterOpenBeforeRegister() throws Exception testInstance.deviceClientArray.get(i).setConnectionStatusChangeCallback(connectionStatusChangeTrackers[i], testInstance.deviceClientArray.get(i).getConfig().getDeviceId()); } - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); // Disable a device that will be on the multiplexed connection Device deviceToDisable = registryClient.getDevice(testInstance.deviceIdentityArray.get(0).getDeviceId()); @@ -1755,7 +1748,7 @@ public void registrationsUnwindForUnsupportedOperationExceptions(DeviceClient un public void failedRegistrationDoesNotAffectSubsequentRegistrations() throws Exception { testInstance.setup(0); - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); TestDeviceIdentity testDeviceIdentity = Tools.getTestDevice(iotHubConnectionString, this.testInstance.protocol, AuthenticationType.SAS, false); @@ -1810,7 +1803,7 @@ public void multiplexedSessionsRecoverSubscriptionsFromDeviceSessionDrops() thro testInstance.deviceClientArray.get(i).setConnectionStatusChangeCallback(connectionStatusChangeTrackers[i], testInstance.deviceClientArray.get(i).getConfig().getDeviceId()); } - testInstance.multiplexingClient.open(false); + testInstance.multiplexingClient.open(true); // Subscribe to methods for all multiplexed clients DirectMethodCallback[] deviceDirectMethodCallbacks = new DirectMethodCallback[DEVICE_MULTIPLEX_COUNT]; diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/TokenRenewalTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/TokenRenewalTests.java index 9678a1186f..f3c99da36c 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/TokenRenewalTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/TokenRenewalTests.java @@ -273,12 +273,12 @@ private MultiplexingClient createMultiplexedClientToTest(IotHubClientProtocol pr return multiplexingClient; } - private void sendMessageFromEachClient(List clients) + private void sendMessageFromEachClient(List clients) throws IotHubClientException, InterruptedException { for (InternalClient client : clients) { System.out.println("Sending test message for client " + client.getConfig().getDeviceId()); - IotHubServicesCommon.sendMessageAndWaitForResponse(client, new MessageAndResult(new Message("some message"), IotHubStatusCode.OK), client.getConfig().getProtocol()); + client.sendEvent(new Message("some message")); } } @@ -289,7 +289,8 @@ private void openEachClient(List clients) throws IOException, Io try { client.open(false); - } catch (UnsupportedOperationException ex) + } + catch (UnsupportedOperationException ex) { //client was a multiplexing client which was already opened, safe to ignore } diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/connection/ConnectionTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/connection/ConnectionTests.java new file mode 100644 index 0000000000..254eab6c82 --- /dev/null +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/connection/ConnectionTests.java @@ -0,0 +1,286 @@ +package tests.integration.com.microsoft.azure.sdk.iot.iothub.connection; + +import com.microsoft.azure.sdk.iot.device.*; +import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; +import com.microsoft.azure.sdk.iot.service.auth.IotHubConnectionStringBuilder; +import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException; +import com.microsoft.azure.sdk.iot.service.registry.Device; +import com.microsoft.azure.sdk.iot.service.registry.Module; +import com.microsoft.azure.sdk.iot.service.registry.RegistryClient; +import lombok.extern.slf4j.Slf4j; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.*; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.IotHubTest; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.StandardTierHubOnlyTest; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.proxy.HttpProxyServer; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.proxy.impl.DefaultHttpProxyServer; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.security.GeneralSecurityException; +import java.util.*; + +import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; +import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; +import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SELF_SIGNED; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +@Slf4j +@IotHubTest +@RunWith(Parameterized.class) +public class ConnectionTests extends IntegrationTest +{ + private static String iotHubConnectionString; + private static String hostName; + + @Parameterized.Parameters(name = "{0}_{1}_{2}_{3}") + public static Collection inputs() throws Exception + { + iotHubConnectionString = Tools.retrieveEnvironmentVariableValue(TestConstants.IOT_HUB_CONNECTION_STRING_ENV_VAR_NAME); + hostName = IotHubConnectionStringBuilder.createIotHubConnectionString(iotHubConnectionString).getHostName(); + + return Arrays.asList( + new Object[][] + { + {HTTPS, SAS, ClientType.DEVICE_CLIENT, false}, + {AMQPS, SAS, ClientType.DEVICE_CLIENT, false}, + {AMQPS_WS, SAS, ClientType.DEVICE_CLIENT, false}, + {MQTT, SAS, ClientType.DEVICE_CLIENT, false}, + {MQTT_WS, SAS, ClientType.DEVICE_CLIENT, false}, + + {HTTPS, SELF_SIGNED, ClientType.DEVICE_CLIENT, false}, + {AMQPS, SELF_SIGNED, ClientType.DEVICE_CLIENT, false}, + {MQTT, SELF_SIGNED, ClientType.DEVICE_CLIENT, false}, + {MQTT_WS, SELF_SIGNED, ClientType.DEVICE_CLIENT, false}, + + {AMQPS, SAS, ClientType.MODULE_CLIENT, false}, + {AMQPS_WS, SAS, ClientType.MODULE_CLIENT, false}, + {MQTT, SAS, ClientType.MODULE_CLIENT, false}, + {MQTT_WS, SAS, ClientType.MODULE_CLIENT, false}, + + {AMQPS, SELF_SIGNED, ClientType.MODULE_CLIENT, false}, + {MQTT, SELF_SIGNED, ClientType.MODULE_CLIENT, false}, + {MQTT_WS, SELF_SIGNED, ClientType.MODULE_CLIENT, false}, + + {HTTPS, SAS, ClientType.DEVICE_CLIENT, true}, + {AMQPS_WS, SAS, ClientType.DEVICE_CLIENT, true}, + {MQTT_WS, SAS, ClientType.DEVICE_CLIENT, true}, + {MQTT_WS, SELF_SIGNED, ClientType.DEVICE_CLIENT, true}, + {AMQPS_WS, SAS, ClientType.MODULE_CLIENT, true}, + {MQTT_WS, SAS, ClientType.MODULE_CLIENT, true}, + {MQTT_WS, SELF_SIGNED, ClientType.MODULE_CLIENT, true}, + }); + } + + public ConnectionTests(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean withProxy) + { + this.testInstance = new ConnectionTestInstance(protocol, authenticationType, clientType, withProxy); + } + + public class ConnectionTestInstance + { + public IotHubClientProtocol protocol; + public TestIdentity identity; + public AuthenticationType authenticationType; + public ClientType clientType; + public boolean useHttpProxy; + + public ConnectionTestInstance(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean useHttpProxy) + { + this.protocol = protocol; + this.authenticationType = authenticationType; + this.clientType = clientType; + this.useHttpProxy = useHttpProxy; + } + + public void setup() throws Exception + { + ClientOptions.ClientOptionsBuilder optionsBuilder = ClientOptions.builder(); + if (this.useHttpProxy) + { + Proxy testProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(testProxyHostname, testProxyPort)); + optionsBuilder.proxySettings(new ProxySettings(testProxy, testProxyUser, testProxyPass)); + } + + if (clientType == ClientType.DEVICE_CLIENT) + { + this.identity = Tools.getTestDevice(iotHubConnectionString, this.protocol, this.authenticationType, false, optionsBuilder); + } + else if (clientType == ClientType.MODULE_CLIENT) + { + this.identity = Tools.getTestModule(iotHubConnectionString, this.protocol, this.authenticationType , false, optionsBuilder); + } + } + + public void setupEccDevice() throws Exception + { + ClientOptions.ClientOptionsBuilder optionsBuilder = ClientOptions.builder(); + if (this.useHttpProxy) + { + Proxy testProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(testProxyHostname, testProxyPort)); + optionsBuilder.proxySettings(new ProxySettings(testProxy, testProxyUser, testProxyPass)); + } + + X509CertificateGenerator certificateGenerator = new X509CertificateGenerator(X509CertificateGenerator.CertificateAlgorithm.ECC); + SSLContext sslContext = SSLContextBuilder.buildSSLContext(certificateGenerator.getX509Certificate(), certificateGenerator.getPrivateKey()); + optionsBuilder.sslContext(sslContext); + + if (clientType == ClientType.DEVICE_CLIENT) + { + Device eccDevice = new Device("ecc-test-device-" + UUID.randomUUID(), SELF_SIGNED); + eccDevice.setThumbprint(certificateGenerator.getX509Thumbprint(), certificateGenerator.getX509Thumbprint()); + + Tools.addDeviceWithRetry(new RegistryClient(iotHubConnectionString), eccDevice); + + String deviceConnectionString = Tools.getDeviceConnectionString(iotHubConnectionString, eccDevice); + this.identity = new TestDeviceIdentity( + new DeviceClient(deviceConnectionString, testInstance.protocol, optionsBuilder.build()), + eccDevice); + } + else if (clientType == ClientType.MODULE_CLIENT) + { + Device eccDevice = new Device("ecc-test-device-" + UUID.randomUUID(), SELF_SIGNED); + Module eccModule = new Module(eccDevice.getDeviceId(), "ecc-test-module-" + UUID.randomUUID(), SELF_SIGNED); + eccDevice.setThumbprint(certificateGenerator.getX509Thumbprint(), certificateGenerator.getX509Thumbprint()); + eccModule.setThumbprint(certificateGenerator.getX509Thumbprint(), certificateGenerator.getX509Thumbprint()); + + Tools.addDeviceWithRetry(new RegistryClient(iotHubConnectionString), eccDevice); + Tools.addModuleWithRetry(new RegistryClient(iotHubConnectionString), eccModule); + + String moduleConnectionString = Tools.getDeviceConnectionString(iotHubConnectionString, eccDevice) + ";ModuleId=" + eccModule.getId(); + this.identity = new TestModuleIdentity( + new ModuleClient(moduleConnectionString, testInstance.protocol, optionsBuilder.build()), + eccDevice, + eccModule); + } + } + + public void dispose() + { + if (this.identity != null && this.identity.getClient() != null) + { + this.identity.getClient().close(); + } + + Tools.disposeTestIdentity(this.identity, iotHubConnectionString); + } + } + + private final ConnectionTestInstance testInstance; + protected static HttpProxyServer proxyServer; + protected static String testProxyHostname = "127.0.0.1"; + protected static int testProxyPort = 8899; + + // Semmle flags this as a security issue, but this is a test username so the warning can be suppressed + protected static final String testProxyUser = "proxyUsername"; // lgtm + + // Semmle flags this as a security issue, but this is a test password so the warning can be suppressed + protected static final char[] testProxyPass = "1234".toCharArray(); // lgtm + + @BeforeClass + public static void startProxy() + { + proxyServer = DefaultHttpProxyServer.bootstrap() + .withPort(testProxyPort) + .withProxyAuthenticator(new BasicProxyAuthenticator(testProxyUser, testProxyPass)) + .start(); + } + + @AfterClass + public static void stopProxy() + { + proxyServer.stop(); + } + + @Test(timeout = 60000) // 1 minute + @IotHubTest + public void CanOpenConnection() throws Exception + { + testInstance.setup(); + testInstance.identity.getClient().open(true); + + // deviceClient.open() is a no-op on HTTP, so a message needs to be sent to actually test opening the connection + if (testInstance.protocol == HTTPS) + { + testInstance.identity.getClient().sendEvent(new Message("some message")); + } + + testInstance.identity.getClient().close(); + } + + @IotHubTest + @StandardTierHubOnlyTest + @Test(timeout = 60000) // 1 minute + public void CanOpenConnectionWithECCCertificates() throws Exception + { + // SAS token authenticated devices/modules don't use RSA or ECC certificates + assumeTrue(testInstance.authenticationType == SELF_SIGNED); + + // ECC cert generation is broken for Android. "ECDSA KeyPairGenerator is not available" + assumeFalse(Tools.isAndroid()); + + testInstance.setupEccDevice(); + + testInstance.identity.getClient().open(true); + + // deviceClient.open() is a no-op on HTTP, so a message needs to be sent to actually test opening the connection + if (testInstance.protocol == HTTPS) + { + testInstance.identity.getClient().sendEvent(new Message("some message")); + } + + testInstance.identity.getClient().close(); + } + + @Test + @IotHubTest + public void CanOpenMultiplexingConnection() throws Exception + { + // MQTT/HTTP don't support multiplexing + assumeTrue(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS); + + // IoT hub does not support x509 authenticated multiplexed connections + assumeTrue(testInstance.authenticationType == SAS); + + // IoT hub does not support module multiplexing + assumeTrue(testInstance.clientType == ClientType.DEVICE_CLIENT); + + MultiplexingClientOptions.MultiplexingClientOptionsBuilder optionsBuilder = MultiplexingClientOptions.builder(); + if (testInstance.useHttpProxy) + { + Proxy testProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(testProxyHostname, testProxyPort)); + optionsBuilder.proxySettings(new ProxySettings(testProxy, testProxyUser, testProxyPass)); + } + + MultiplexingClient multiplexingClient = new MultiplexingClient(hostName, testInstance.protocol, optionsBuilder.build()); + + int multiplexCount = 3; + List testIdentities = new ArrayList<>(multiplexCount); + List testClients = new ArrayList<>(multiplexCount); + for (int i = 0; i < multiplexCount; i++) + { + TestIdentity testIdentity = Tools.getTestDevice(iotHubConnectionString, testInstance.protocol, testInstance.authenticationType, false); + testIdentities.add(testIdentity); + testClients.add((DeviceClient) testIdentity.getClient()); + } + + try + { + multiplexingClient.registerDeviceClients(testClients); + + multiplexingClient.open(true); + multiplexingClient.close(); + } + finally + { + Tools.disposeTestIdentities(testIdentities, iotHubConnectionString); + } + } +} diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/DirectMethodsErrInjTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/DirectMethodsErrInjTests.java index 26179ead44..8c66b48cf6 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/DirectMethodsErrInjTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/DirectMethodsErrInjTests.java @@ -29,6 +29,7 @@ import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SELF_SIGNED; +import static org.junit.Assume.assumeTrue; /** * Test class containing all error injection tests to be run on JVM and android pertaining to Device methods. @@ -58,10 +59,7 @@ public void invokeMethodRecoveredFromTcpConnectionDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsConnectionDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.tcpConnectionDropErrorInjectionMessage( @@ -74,10 +72,7 @@ public void invokeMethodRecoveredFromAmqpsConnectionDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsSessionDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsSessionDropErrorInjectionMessage( @@ -90,16 +85,10 @@ public void invokeMethodRecoveredFromAmqpsSessionDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsCBSReqLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS); - if (testInstance.authenticationType != SAS) - { - //CBS links are only established when using sas authentication - return; - } + //CBS links are only established when using sas authentication + assumeTrue(testInstance.authenticationType == SAS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsCBSReqLinkDropErrorInjectionMessage( @@ -112,16 +101,10 @@ public void invokeMethodRecoveredFromAmqpsCBSReqLinkDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsCBSRespLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS); - if (testInstance.authenticationType != SAS) - { - //CBS links are only established when using sas authentication - return; - } + //CBS links are only established when using sas authentication + assumeTrue(testInstance.authenticationType == SAS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsCBSRespLinkDropErrorInjectionMessage( @@ -134,10 +117,7 @@ public void invokeMethodRecoveredFromAmqpsCBSRespLinkDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsD2CLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsD2CTelemetryLinkDropErrorInjectionMessage( @@ -150,17 +130,12 @@ public void invokeMethodRecoveredFromAmqpsD2CLinkDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsC2DLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } - - if (testInstance.protocol == AMQPS && testInstance.authenticationType == SELF_SIGNED) - { - //TODO error injection seems to fail under these circumstances. C2D link is never dropped even if waiting a long time - // Need to talk to service folks about this strange behavior - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); + + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsC2DLinkDropErrorInjectionMessage( @@ -173,17 +148,12 @@ public void invokeMethodRecoveredFromAmqpsC2DLinkDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsMethodReqLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } - - if (testInstance.protocol == AMQPS && testInstance.authenticationType == SELF_SIGNED) - { - //TODO error injection seems to fail under these circumstances. Method Req is never dropped even if waiting a long time - // Need to talk to service folks about this strange behavior - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); + + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsMethodRespLinkDropErrorInjectionMessage( @@ -196,17 +166,12 @@ public void invokeMethodRecoveredFromAmqpsMethodReqLinkDrop() throws Exception @ContinuousIntegrationTest public void invokeMethodRecoveredFromAmqpsMethodRespLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } - - if (testInstance.protocol == AMQPS && testInstance.authenticationType == SELF_SIGNED) - { - //TODO error injection seems to fail under these circumstances. Method Resp is never dropped even if waiting a long time - // Need to talk to service folks about this strange behavior - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); + + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsMethodRespLinkDropErrorInjectionMessage( @@ -218,10 +183,7 @@ public void invokeMethodRecoveredFromAmqpsMethodRespLinkDrop() throws Exception @StandardTierHubOnlyTest public void invokeMethodRecoveredFromGracefulShutdownAmqp() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsGracefulShutdownErrorInjectionMessage( @@ -233,10 +195,7 @@ public void invokeMethodRecoveredFromGracefulShutdownAmqp() throws Exception @StandardTierHubOnlyTest public void invokeMethodRecoveredFromGracefulShutdownMqtt() throws Exception { - if (!(testInstance.protocol == MQTT || testInstance.protocol == MQTT_WS)) - { - return; - } + assumeTrue(this.testInstance.protocol == MQTT || this.testInstance.protocol == MQTT_WS); super.openDeviceClientAndSubscribeToMethods(); this.errorInjectionTestFlow(ErrorInjectionHelper.mqttGracefulShutdownErrorInjectionMessage( diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/ReceiveMessagesErrInjTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/ReceiveMessagesErrInjTests.java index 56cee57c18..805d0f9c1f 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/ReceiveMessagesErrInjTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/ReceiveMessagesErrInjTests.java @@ -7,11 +7,9 @@ import com.microsoft.azure.sdk.iot.device.*; -import com.microsoft.azure.sdk.iot.device.exceptions.IotHubClientException; -import com.microsoft.azure.sdk.iot.device.twin.Pair; import com.microsoft.azure.sdk.iot.device.transport.IotHubConnectionStatus; +import com.microsoft.azure.sdk.iot.device.twin.Pair; import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; -import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -22,14 +20,13 @@ import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.StandardTierHubOnlyTest; import tests.integration.com.microsoft.azure.sdk.iot.iothub.setup.ReceiveMessagesCommon; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeoutException; import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; -import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.*; +import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; /** * Test class containing all error injection tests to be run on JVM and android pertaining to receiving messages. @@ -48,11 +45,8 @@ public ReceiveMessagesErrInjTests(IotHubClientProtocol protocol, AuthenticationT @StandardTierHubOnlyTest public void receiveMessagesWithTCPConnectionDrop() throws Exception { - if (testInstance.protocol == HTTPS) - { - //test case not applicable - return; - } + // This error injection test only applies for no connection can be dropped for HTTP since it is a stateless connection + assumeTrue(this.testInstance.protocol != HTTPS); super.setupTest(); this.errorInjectionTestFlow(ErrorInjectionHelper.tcpConnectionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); @@ -63,11 +57,7 @@ public void receiveMessagesWithTCPConnectionDrop() throws Exception @ContinuousIntegrationTest public void receiveMessagesWithAmqpsConnectionDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS) - { - //test case not applicable - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); super.setupTest(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsConnectionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); @@ -78,11 +68,7 @@ public void receiveMessagesWithAmqpsConnectionDrop() throws Exception @ContinuousIntegrationTest public void receiveMessagesWithAmqpsSessionDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS) - { - //test case not applicable - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); super.setupTest(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsSessionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); @@ -93,17 +79,8 @@ public void receiveMessagesWithAmqpsSessionDrop() throws Exception @ContinuousIntegrationTest public void receiveMessagesWithAmqpsCBSReqLinkDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS) - { - //test case not applicable - return; - } - - if (testInstance.authenticationType == SELF_SIGNED || testInstance.authenticationType == CERTIFICATE_AUTHORITY) - { - //cbs links aren't established in these scenarios, so it would be impossible/irrelevant if a cbs link dropped - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); + assumeTrue(this.testInstance.authenticationType == SAS); super.setupTest(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsCBSReqLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); @@ -114,17 +91,8 @@ public void receiveMessagesWithAmqpsCBSReqLinkDrop() throws Exception @ContinuousIntegrationTest public void receiveMessagesWithAmqpsCBSRespLinkDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS) - { - //test case not applicable - return; - } - - if (testInstance.authenticationType == SELF_SIGNED || testInstance.authenticationType == CERTIFICATE_AUTHORITY) - { - //cbs links aren't established in these scenarios, so it would be impossible/irrelevant if a cbs link dropped - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); + assumeTrue(this.testInstance.authenticationType == SAS); super.setupTest(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsCBSRespLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); @@ -135,11 +103,7 @@ public void receiveMessagesWithAmqpsCBSRespLinkDrop() throws Exception @ContinuousIntegrationTest public void receiveMessagesWithAmqpsD2CLinkDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS) - { - //test case not applicable - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); super.setupTest(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsD2CTelemetryLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); @@ -150,114 +114,38 @@ public void receiveMessagesWithAmqpsD2CLinkDrop() throws Exception @ContinuousIntegrationTest public void receiveMessagesWithAmqpsC2DLinkDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS) - { - //test case not applicable - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - if (testInstance.authenticationType != SAS) - { - //TODO X509 case never seems to get callback about the connection dying. Needs investigation because this should pass, but doesn't - return; - } + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); super.setupTest(); this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsC2DLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } - @Test - @StandardTierHubOnlyTest - public void receiveMessagesWithGracefulShutdownAmqp() throws Exception - { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS) - { - //test case not applicable - return; - } - - super.setupTest(); - this.errorInjectionTestFlow(ErrorInjectionHelper.amqpsGracefulShutdownErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); - } - - @Test - @StandardTierHubOnlyTest - public void receiveMessagesWithGracefulShutdownMqtt() throws Exception - { - if (testInstance.protocol != MQTT && testInstance.protocol != MQTT_WS) - { - //test case not applicable - return; - } - - super.setupTest(); - this.errorInjectionTestFlow(ErrorInjectionHelper.mqttGracefulShutdownErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); - } - - public void errorInjectionTestFlow(com.microsoft.azure.sdk.iot.device.Message errorInjectionMessage) throws IOException, IotHubException, InterruptedException, TimeoutException, IotHubClientException + public void errorInjectionTestFlow(com.microsoft.azure.sdk.iot.device.Message faultInjectionMessage) throws Exception { - List> connectionStatusUpdates = new ArrayList<>(); - testInstance.identity.getClient().setConnectionStatusChangeCallback((context) -> connectionStatusUpdates.add(new Pair<>(context.getNewStatus(), context.getCause())), null); - - com.microsoft.azure.sdk.iot.device.MessageCallback callback = new MessageCallback(); - - if (testInstance.protocol == MQTT || testInstance.protocol == MQTT_WS) - { - callback = new MessageCallbackMqtt(); - } - - Success messageReceived = new Success(); - if (testInstance.identity.getClient() instanceof DeviceClient) - { - ((DeviceClient) testInstance.identity.getClient()).setMessageCallback(callback, messageReceived); - } - else if (testInstance.identity.getClient() instanceof ModuleClient) - { - ((ModuleClient) testInstance.identity.getClient()).setMessageCallback(callback, messageReceived); - } - - try - { - testInstance.identity.getClient().open(false); - - IotHubStatusCode expectedStatusCode = IotHubStatusCode.OK; - - if (testInstance.protocol == MQTT || testInstance.protocol == MQTT_WS) - { - // error injection message will not be ack'd by service if sent over MQTT/MQTT_WS, so the SDK's - // retry logic will try to send it again after the connection drops. By setting expiry time, - // we ensure that error injection message isn't resent to service too many times. The message will still likely - // be sent 3 or 4 times causing 3 or 4 disconnections, but the test should recover anyways. - errorInjectionMessage.setExpiryTime(1000); + List> actualStatusUpdates = new ArrayList<>(); + IotHubConnectionStatusChangeCallback connectionStatusUpdateCallback = (context) -> actualStatusUpdates.add(new Pair<>(context.getNewStatus(), context.getCause())); + testInstance.identity.getClient().setConnectionStatusChangeCallback(connectionStatusUpdateCallback, null); - // Since the message won't be ack'd, then we don't need to validate the status code when this message's callback is fired - expectedStatusCode = null; - } + testInstance.identity.getClient().open(true); - testInstance.identity.getClient().sendEventAsync(errorInjectionMessage, new EventCallback(expectedStatusCode), null); + // Test that the device/module can receive c2d messages + receiveMessage(MESSAGE_SIZE_IN_BYTES, false); - //wait to send the message because we want to ensure that the tcp connection drop happens beforehand and we - // want the connection to be re-established before sending anything from service client - IotHubServicesCommon.waitForStabilizedConnection(connectionStatusUpdates, testInstance.identity.getClient()); + // Inject the error + MessageAndResult errorInjectionMsgAndRet = new MessageAndResult(faultInjectionMessage, IotHubStatusCode.OK); + IotHubServicesCommon.sendErrorInjectionMessageAndWaitForResponse(testInstance.identity.getClient(), errorInjectionMsgAndRet, testInstance.protocol); - if (testInstance.identity.getClient() instanceof DeviceClient) - { - sendMessageToDevice(testInstance.identity.getDeviceId(), MESSAGE_SIZE_IN_BYTES); - } - else if (testInstance.identity.getClient() instanceof ModuleClient) - { - sendMessageToModule(testInstance.identity.getDeviceId(), ((TestModuleIdentity) testInstance.identity).getModuleId(), MESSAGE_SIZE_IN_BYTES); - } + IotHubServicesCommon.waitForStabilizedConnection(actualStatusUpdates, testInstance.identity.getClient()); - waitForMessageToBeReceived(messageReceived, testInstance.protocol.toString()); + // Test that the device/module can still receive c2d messages + receiveMessage(MESSAGE_SIZE_IN_BYTES, false); - Thread.sleep(200); - } - finally - { - testInstance.identity.getClient().close(); - } + testInstance.identity.getClient().close(); - assertTrue(CorrelationDetailsLoggingAssert.buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Error Injection message did not cause service to drop TCP connection", testInstance.identity.getClient()), IotHubServicesCommon.actualStatusUpdatesContainsStatus(connectionStatusUpdates, IotHubConnectionStatus.DISCONNECTED_RETRYING)); + assertTrue(CorrelationDetailsLoggingAssert.buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Error Injection message did not cause service to drop TCP connection", testInstance.identity.getClient()), IotHubServicesCommon.actualStatusUpdatesContainsStatus(actualStatusUpdates, IotHubConnectionStatus.DISCONNECTED_RETRYING)); } } diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/SendMessagesErrInjTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/SendMessagesErrInjTests.java index 471fc5991a..e0629b512f 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/SendMessagesErrInjTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/SendMessagesErrInjTests.java @@ -11,8 +11,10 @@ import com.microsoft.azure.sdk.iot.device.transport.ExponentialBackoffWithJitter; import com.microsoft.azure.sdk.iot.device.transport.IotHubConnectionStatus; import com.microsoft.azure.sdk.iot.device.transport.NoRetry; +import com.microsoft.azure.sdk.iot.device.twin.Pair; import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException; +import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,10 +28,17 @@ import java.io.IOException; import java.net.URISyntaxException; import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.List; import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SELF_SIGNED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; +import static tests.integration.com.microsoft.azure.sdk.iot.helpers.CorrelationDetailsLoggingAssert.buildExceptionMessage; +import static tests.integration.com.microsoft.azure.sdk.iot.helpers.IotHubServicesCommon.actualStatusUpdatesContainsStatus; /** * Test class containing all error injection tests to be run on JVM and android pertaining to sending messages to the cloud. @@ -39,144 +48,96 @@ @RunWith(Parameterized.class) public class SendMessagesErrInjTests extends SendMessagesCommon { - public SendMessagesErrInjTests(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean withProxy) throws Exception + public SendMessagesErrInjTests(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) throws Exception { - super(protocol, authenticationType, clientType, withProxy); + super(protocol, authenticationType, clientType); } @Test public void sendMessagesWithTcpConnectionDrop() throws Exception { - if (testInstance.protocol == HTTPS || (testInstance.protocol == MQTT_WS && testInstance.authenticationType != SAS) || testInstance.useHttpProxy) - { - //TCP connection is not maintained between device and service when using HTTPS, so this test case isn't applicable - //MQTT_WS + x509 is not supported for sending messages - return; - } + // This error injection test only applies for no connection can be dropped for HTTP since it is a stateless connection + assumeTrue(this.testInstance.protocol != HTTPS); - this.testInstance.setup(); - - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, TCP_CONNECTION_DROP_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + errorInjectionTestFlow(ErrorInjectionHelper.tcpConnectionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } @Test @ContinuousIntegrationTest public void sendMessagesOverAmqpWithConnectionDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || (testInstance.protocol == AMQPS_WS && testInstance.authenticationType == SAS)) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS with SAS and X509 and for AMQPS_WS with SAS - return; - } - - this.testInstance.setup(); + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, AMQP_CONNECTION_DROP_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + errorInjectionTestFlow(ErrorInjectionHelper.amqpsConnectionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } @Test @ContinuousIntegrationTest public void sendMessagesOverAmqpWithSessionDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || (testInstance.protocol == AMQPS_WS && testInstance.authenticationType == SAS)) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS with SAS and X509 and for AMQPS_WS with SAS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - this.testInstance.setup(); - - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, AMQP_SESSION_DROP_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + errorInjectionTestFlow(ErrorInjectionHelper.amqpsSessionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } @Test @ContinuousIntegrationTest public void sendMessagesOverAmqpWithCbsRequestLinkDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS and AMQPS_WS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - if (testInstance.authenticationType != SAS) - { - //CBS links are only established when using sas authentication - return; - } - - this.testInstance.setup(); + //CBS links are only established when using sas authentication + assumeTrue(testInstance.authenticationType == SAS); - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, AMQP_CBS_REQUEST_LINK_DROP_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + errorInjectionTestFlow(ErrorInjectionHelper.amqpsCBSReqLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } @Test @ContinuousIntegrationTest public void sendMessagesOverAmqpWithCbsResponseLinkDrop() throws Exception { - if (testInstance.protocol != AMQPS && testInstance.protocol != AMQPS_WS || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS and AMQPS_WS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - if (testInstance.authenticationType != SAS) - { - //CBS links are only established when using sas authentication - return; - } - - this.testInstance.setup(); + //CBS links are only established when using sas authentication + assumeTrue(testInstance.authenticationType == SAS); - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, AMQP_CBS_RESPONSE_LINK_DROP_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + errorInjectionTestFlow(ErrorInjectionHelper.amqpsCBSRespLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } @Test @ContinuousIntegrationTest public void sendMessagesOverAmqpWithD2CLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || (testInstance.protocol == AMQPS_WS && testInstance.authenticationType == SAS)) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS with SAS and X509 and for AMQPS_WS with SAS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - this.testInstance.setup(); - - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, AMQP_D2C_LINK_DROP_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + errorInjectionTestFlow(ErrorInjectionHelper.amqpsD2CTelemetryLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } @Test @ContinuousIntegrationTest public void sendMessagesOverAmqpWithC2DLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || (testInstance.protocol == AMQPS_WS && testInstance.authenticationType == SAS)) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS with SAS and X509 and for AMQPS_WS with SAS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - if (testInstance.protocol == AMQPS && testInstance.authenticationType == SELF_SIGNED) - { - //TODO error injection seems to fail under these circumstances. C2D link is never dropped even if waiting a long time - // Need to talk to service folks about this strange behavior - return; - } - - this.testInstance.setup(); + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, AMQP_C2D_LINK_DROP_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + errorInjectionTestFlow(ErrorInjectionHelper.amqpsC2DLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } @Ignore @Test public void sendMessagesWithThrottling() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS and AMQPS_WS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); this.testInstance.setup(); @@ -184,7 +145,6 @@ public void sendMessagesWithThrottling() throws Exception ErrorInjectionHelper.throttledConnectionErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec), IotHubStatusCode.OK, false); - } @Ignore @@ -192,11 +152,8 @@ public void sendMessagesWithThrottling() throws Exception @ContinuousIntegrationTest public void sendMessagesWithThrottlingNoRetry() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS and AMQPS_WS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); this.testInstance.setup(); @@ -204,18 +161,14 @@ public void sendMessagesWithThrottlingNoRetry() throws Exception ErrorInjectionHelper.throttledConnectionErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec), IotHubStatusCode.THROTTLED, true); - } @Ignore @Test public void sendMessagesWithAuthenticationError() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS and AMQPS_WS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); this.testInstance.setup(); @@ -230,11 +183,8 @@ public void sendMessagesWithAuthenticationError() throws Exception @ContinuousIntegrationTest public void sendMessagesWithQuotaExceeded() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS and AMQPS_WS - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); this.testInstance.setup(); @@ -245,53 +195,61 @@ public void sendMessagesWithQuotaExceeded() throws Exception } @Test - public void sendMessagesOverAmqpWithGracefulShutdown() throws Exception + @ContinuousIntegrationTest + public void sendMessagesWithTcpConnectionDropNotifiesUserIfRetryExpires() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS) || testInstance.useHttpProxy) - { - //This error injection test only applies for AMQPS and AMQPS_WS - return; - } + // This error injection test only applies for no connection can be dropped for HTTP since it is a stateless connection + assumeTrue(this.testInstance.protocol != HTTPS); this.testInstance.setup(); - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, AMQP_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); - } + testInstance.identity.getClient().setRetryPolicy(new NoRetry()); - @Test - public void sendMessagesOverMqttWithGracefulShutdown() throws Exception - { - if (!(testInstance.protocol == MQTT || testInstance.protocol == MQTT_WS) || testInstance.useHttpProxy) + Message errorInjectionMessage = ErrorInjectionHelper.tcpConnectionDropErrorInjectionMessage(1, 100000); + final List> statusUpdates = new ArrayList<>(); + testInstance.identity.getClient().setConnectionStatusChangeCallback((context) -> statusUpdates.add(new Pair<>(context.getNewStatus(), context.getCause())), new Object()); + + testInstance.identity.getClient().open(false); + + testInstance.identity.getClient().sendEventAsync(errorInjectionMessage, new EventCallback(null), new Success()); + + long startTime = System.currentTimeMillis(); + while (!(actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED_RETRYING) && actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED))) { - //This error injection test only applies for MQTT and MQTT_WS - return; + Thread.sleep(500); + + if (System.currentTimeMillis() - startTime > 30 * 1000) + { + break; + } } - this.testInstance.setup(); + Assert.assertTrue(buildExceptionMessage("Expected notification about disconnected but retrying.", testInstance.identity.getClient()), actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED_RETRYING)); + Assert.assertTrue(buildExceptionMessage("Expected notification about disconnected.", testInstance.identity.getClient()), actualStatusUpdatesContainsStatus(statusUpdates, IotHubConnectionStatus.DISCONNECTED)); - IotHubServicesCommon.sendMessagesExpectingConnectionStatusChangeUpdate(testInstance.identity.getClient(), testInstance.protocol, MQTT_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, IotHubConnectionStatus.DISCONNECTED_RETRYING, 100, testInstance.authenticationType); + testInstance.identity.getClient().close(); } - @Test - @ContinuousIntegrationTest - public void sendMessagesWithTcpConnectionDropNotifiesUserIfRetryExpires() throws Exception + private void errorInjectionTestFlow(Message faultInjectionMessage) throws Exception { - if (testInstance.protocol == HTTPS || (testInstance.protocol == MQTT_WS && testInstance.authenticationType != SAS) || testInstance.useHttpProxy) - { - //TCP connection is not maintained between device and service when using HTTPS, so this test case isn't applicable - //MQTT_WS + x509 is not supported for sending messages - return; - } + testInstance.setup(); - this.testInstance.setup(); + List> actualStatusUpdates = new ArrayList<>(); + IotHubConnectionStatusChangeCallback connectionStatusUpdateCallback = (context) -> actualStatusUpdates.add(new Pair<>(context.getNewStatus(), context.getCause())); + testInstance.identity.getClient().setConnectionStatusChangeCallback(connectionStatusUpdateCallback, null); - testInstance.identity.getClient().setRetryPolicy(new NoRetry()); + testInstance.identity.getClient().open(true); + + testInstance.identity.getClient().sendEvent(new Message("some normal message")); - Message tcpConnectionDropErrorInjectionMessageUnrecoverable = ErrorInjectionHelper.tcpConnectionDropErrorInjectionMessage(1, 100000); - IotHubServicesCommon.sendMessagesExpectingUnrecoverableConnectionLossAndTimeout(testInstance.identity.getClient(), testInstance.protocol, tcpConnectionDropErrorInjectionMessageUnrecoverable, testInstance.authenticationType); + MessageAndResult errorInjectionMsgAndRet = new MessageAndResult(faultInjectionMessage, IotHubStatusCode.OK); + IotHubServicesCommon.sendErrorInjectionMessageAndWaitForResponse(testInstance.identity.getClient(), errorInjectionMsgAndRet, testInstance.protocol); - //reset back to default - testInstance.identity.getClient().setRetryPolicy(new ExponentialBackoffWithJitter()); + IotHubServicesCommon.waitForStabilizedConnection(actualStatusUpdates, testInstance.identity.getClient()); + + testInstance.identity.getClient().sendEvent(new Message("some normal message")); + + testInstance.identity.getClient().close(); } private void errorInjectionTestFlowNoDisconnect(Message errorInjectionMessage, IotHubStatusCode expectedStatus, boolean noRetry) throws IOException, IotHubException, URISyntaxException, InterruptedException, GeneralSecurityException, IotHubClientException @@ -301,23 +259,29 @@ private void errorInjectionTestFlowNoDisconnect(Message errorInjectionMessage, I { testInstance.identity.getClient().setRetryPolicy(new NoRetry()); } + testInstance.identity.getClient().open(false); // Act MessageAndResult errorInjectionMsgAndRet = new MessageAndResult(errorInjectionMessage,IotHubStatusCode.OK); - IotHubServicesCommon.sendMessageAndWaitForResponse( - testInstance.identity.getClient(), - errorInjectionMsgAndRet, - this.testInstance.protocol); + IotHubServicesCommon.sendErrorInjectionMessageAndWaitForResponse(testInstance.identity.getClient(), errorInjectionMsgAndRet, testInstance.protocol); // time for the error injection to take effect on the service side Thread.sleep(2000); - MessageAndResult normalMessageAndExpectedResult = new MessageAndResult(new Message("test message"), expectedStatus); - IotHubServicesCommon.sendMessageAndWaitForResponse( - testInstance.identity.getClient(), - normalMessageAndExpectedResult, - this.testInstance.protocol); + try + { + testInstance.identity.getClient().sendEvent(new Message("some message sent after the fault")); + + if (expectedStatus != IotHubStatusCode.OK) + { + fail("Expected " + expectedStatus + " but was OK"); + } + } + catch (IotHubClientException e) + { + assertEquals(expectedStatus, e.getStatusCode()); + } testInstance.identity.getClient().close(); } diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/TwinErrInjTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/TwinErrInjTests.java index 2c2e700eb2..a33869f16d 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/TwinErrInjTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/errorinjection/TwinErrInjTests.java @@ -30,6 +30,7 @@ import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SELF_SIGNED; +import static org.junit.Assume.assumeTrue; /** * Test class containing all error injection tests to be run on JVM and android pertaining to GetDeviceTwin/GetTwin. @@ -58,10 +59,7 @@ public void getDeviceTwinRecoveredFromTcpConnectionDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsConnectionDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsConnectionDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, @@ -73,10 +71,7 @@ public void getDeviceTwinRecoveredFromAmqpsConnectionDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsSessionDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsSessionDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, @@ -88,16 +83,10 @@ public void getDeviceTwinRecoveredFromAmqpsSessionDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsCBSReqLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - if (testInstance.authenticationType != SAS) - { - //CBS links are only established when using sas authentication - return; - } + //CBS links are only established when using sas authentication + assumeTrue(this.testInstance.authenticationType == SAS); this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsCBSReqLinkDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, @@ -109,16 +98,10 @@ public void getDeviceTwinRecoveredFromAmqpsCBSReqLinkDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsCBSRespLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - if (testInstance.authenticationType != SAS) - { - //CBS links are only established when using sas authentication - return; - } + //CBS links are only established when using sas authentication + assumeTrue(this.testInstance.authenticationType == SAS); this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsCBSRespLinkDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, @@ -130,10 +113,7 @@ public void getDeviceTwinRecoveredFromAmqpsCBSRespLinkDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsD2CLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsD2CTelemetryLinkDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, @@ -145,17 +125,12 @@ public void getDeviceTwinRecoveredFromAmqpsD2CLinkDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsC2DLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } - - if (testInstance.protocol == AMQPS && testInstance.authenticationType == SELF_SIGNED) - { - //TODO error injection seems to fail under these circumstances. C2D link is never dropped even if waiting a long time - // Need to talk to service folks about this strange behavior - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); + + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsC2DLinkDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, @@ -167,17 +142,12 @@ public void getDeviceTwinRecoveredFromAmqpsC2DLinkDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsTwinReqLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } - - if (testInstance.protocol == AMQPS && testInstance.authenticationType == SELF_SIGNED) - { - //TODO error injection seems to fail under these circumstances. Twin Req link is never dropped even if waiting a long time - // Need to talk to service folks about this strange behavior - return; - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); + + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsTwinReqLinkDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, @@ -189,47 +159,14 @@ public void getDeviceTwinRecoveredFromAmqpsTwinReqLinkDrop() throws Exception @ContinuousIntegrationTest public void getDeviceTwinRecoveredFromAmqpsTwinRespLinkDrop() throws Exception { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } - - if (testInstance.protocol == AMQPS && testInstance.authenticationType == SELF_SIGNED) - { - //TODO error injection seems to fail under these circumstances. Twin Resp link is never dropped even if waiting a long time - // Need to talk to service folks about this strange behavior - return; - } - - this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsTwinRespLinkDropErrorInjectionMessage( - ErrorInjectionHelper.DefaultDelayInSec, - ErrorInjectionHelper.DefaultDurationInSec)); - } + // This error injection test only applies for AMQPS and AMQPS_WS + assumeTrue(this.testInstance.protocol == AMQPS || this.testInstance.protocol == AMQPS_WS); - @Test - @StandardTierHubOnlyTest - public void getDeviceTwinRecoveredFromGracefulShutdownAmqp() throws Exception - { - if (!(testInstance.protocol == AMQPS || testInstance.protocol == AMQPS_WS)) - { - return; - } + //TODO error injection seems to fail when using x509 auth here. C2D link is never dropped even if waiting a long time + // Need to talk to service folks about this strange behavior + assumeTrue(testInstance.authenticationType == SAS); - this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsGracefulShutdownErrorInjectionMessage( - ErrorInjectionHelper.DefaultDelayInSec, - ErrorInjectionHelper.DefaultDurationInSec)); - } - - @Test - @StandardTierHubOnlyTest - public void getDeviceTwinRecoveredFromGracefulShutdownMqtt() throws Exception - { - if (!(testInstance.protocol == MQTT || testInstance.protocol == MQTT_WS)) - { - return; - } - - this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.mqttGracefulShutdownErrorInjectionMessage( + this.errorInjectionGetDeviceTwinFlow(ErrorInjectionHelper.amqpsTwinRespLinkDropErrorInjectionMessage( ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec)); } diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/methods/DirectMethodsTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/methods/DirectMethodsTests.java index 03c2415d6e..64fd847137 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/methods/DirectMethodsTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/methods/DirectMethodsTests.java @@ -5,47 +5,33 @@ package tests.integration.com.microsoft.azure.sdk.iot.iothub.methods; -import com.azure.core.credential.AzureSasCredential; import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol; -import com.microsoft.azure.sdk.iot.service.ProxyOptions; import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; -import com.microsoft.azure.sdk.iot.service.auth.IotHubConnectionString; -import com.microsoft.azure.sdk.iot.service.auth.IotHubConnectionStringBuilder; -import com.microsoft.azure.sdk.iot.service.auth.IotHubServiceSasToken; import com.microsoft.azure.sdk.iot.service.exceptions.ErrorCodeDescription; import com.microsoft.azure.sdk.iot.service.exceptions.IotHubGatewayTimeoutException; import com.microsoft.azure.sdk.iot.service.exceptions.IotHubNotFoundException; -import com.microsoft.azure.sdk.iot.service.exceptions.IotHubUnauthorizedException; import com.microsoft.azure.sdk.iot.service.methods.DirectMethodRequestOptions; import com.microsoft.azure.sdk.iot.service.methods.DirectMethodResponse; -import com.microsoft.azure.sdk.iot.service.methods.DirectMethodsClient; -import com.microsoft.azure.sdk.iot.service.methods.DirectMethodsClientOptions; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import tests.integration.com.microsoft.azure.sdk.iot.helpers.ClientType; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.SasTokenTools; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.CustomObject; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.NestedCustomObject; import tests.integration.com.microsoft.azure.sdk.iot.helpers.TestModuleIdentity; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.ContinuousIntegrationTest; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.IotHubTest; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.StandardTierHubOnlyTest; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.proxy.HttpProxyServer; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.proxy.impl.DefaultHttpProxyServer; -import tests.integration.com.microsoft.azure.sdk.iot.iothub.setup.CustomObject; import tests.integration.com.microsoft.azure.sdk.iot.iothub.setup.DirectMethodsCommon; -import tests.integration.com.microsoft.azure.sdk.iot.iothub.setup.NestedCustomObject; -import java.net.InetSocketAddress; -import java.net.Proxy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import static junit.framework.TestCase.fail; import static org.junit.Assert.*; import static tests.integration.com.microsoft.azure.sdk.iot.helpers.CorrelationDetailsLoggingAssert.buildExceptionMessage; @@ -70,64 +56,6 @@ public void invokeMethodSucceed() throws Exception super.invokeMethodSucceed(); } - @Test - @StandardTierHubOnlyTest - public void invokeMethodSucceedWithAzureSasCredential() throws Exception - { - this.testInstance.methodServiceClient = buildDeviceMethodClientWithAzureSasCredential(); - super.openDeviceClientAndSubscribeToMethods(); - super.invokeMethodSucceed(); - } - - @Test - @StandardTierHubOnlyTest - public void serviceClientTokenRenewalWithAzureSasCredential() throws Exception - { - if (testInstance.protocol != IotHubClientProtocol.AMQPS - || testInstance.clientType != ClientType.DEVICE_CLIENT - || testInstance.authenticationType != AuthenticationType.SAS) - { - // This test is for the service client, so no need to rerun it for all the different client types or device protocols - return; - } - - IotHubConnectionString iotHubConnectionStringObj = IotHubConnectionStringBuilder.createIotHubConnectionString(iotHubConnectionString); - IotHubServiceSasToken serviceSasToken = new IotHubServiceSasToken(iotHubConnectionStringObj); - AzureSasCredential sasCredential = new AzureSasCredential(serviceSasToken.toString()); - - this.testInstance.methodServiceClient = - new DirectMethodsClient( - iotHubConnectionStringObj.getHostName(), - sasCredential, - DirectMethodsClientOptions.builder().httpReadTimeoutSeconds(HTTP_READ_TIMEOUT).build()); - - super.openDeviceClientAndSubscribeToMethods(); - - // add first device just to make sure that the first credential update worked - super.invokeMethodSucceed(); - - // deliberately expire the SAS token to provoke a 401 to ensure that the method client is using the shared - // access signature that is set here. - sasCredential.update(SasTokenTools.makeSasTokenExpired(serviceSasToken.toString())); - - try - { - super.invokeMethodSucceed(); - fail("Expected invoke method call to throw unauthorized exception since an expired SAS token was used, but no exception was thrown"); - } - catch (IotHubUnauthorizedException e) - { - log.debug("IotHubUnauthorizedException was thrown as expected, continuing test"); - } - - // Renew the expired shared access signature - serviceSasToken = new IotHubServiceSasToken(iotHubConnectionStringObj); - sasCredential.update(serviceSasToken.toString()); - - // final method invocation should succeed since the shared access signature has been renewed - super.invokeMethodSucceed(); - } - @Test @StandardTierHubOnlyTest @ContinuousIntegrationTest @@ -303,41 +231,6 @@ public void invokeMethodOnUnregisteredDevice() throws Exception } } - @Test - @StandardTierHubOnlyTest - public void invokeMethodWithServiceSideProxy() throws Exception - { - if (testInstance.protocol != IotHubClientProtocol.MQTT || testInstance.authenticationType != AuthenticationType.SAS || testInstance.clientType != ClientType.DEVICE_CLIENT) - { - // This test doesn't really care about the device side protocol or authentication, so just run it once - // when the device is using MQTT with SAS auth - return; - } - - String testProxyHostname = "127.0.0.1"; - int testProxyPort = 8894; - HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap() - .withPort(testProxyPort) - .start(); - - try - { - Proxy serviceSideProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(testProxyHostname, testProxyPort)); - - ProxyOptions proxyOptions = new ProxyOptions(serviceSideProxy); - DirectMethodsClientOptions options = DirectMethodsClientOptions.builder().proxyOptions(proxyOptions).httpReadTimeoutSeconds(HTTP_READ_TIMEOUT).build(); - - this.testInstance.methodServiceClient = new DirectMethodsClient(iotHubConnectionString, options); - - super.openDeviceClientAndSubscribeToMethods(); - super.invokeMethodSucceed(); - } - finally - { - proxyServer.stop(); - } - } - @Test @StandardTierHubOnlyTest public void invokeMethodWithPayloadAsNull() throws Exception diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/DirectMethodsCommon.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/DirectMethodsCommon.java index 16cf7a42ff..72e52a74e4 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/DirectMethodsCommon.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/DirectMethodsCommon.java @@ -6,35 +6,26 @@ package tests.integration.com.microsoft.azure.sdk.iot.iothub.setup; -import com.azure.core.credential.AzureSasCredential; -import com.microsoft.azure.sdk.iot.device.*; +import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol; import com.microsoft.azure.sdk.iot.device.twin.DirectMethodPayload; -import com.microsoft.azure.sdk.iot.service.auth.IotHubConnectionString; -import com.microsoft.azure.sdk.iot.service.auth.IotHubConnectionStringBuilder; -import com.microsoft.azure.sdk.iot.service.registry.RegistryClient; -import com.microsoft.azure.sdk.iot.service.registry.RegistryClientOptions; import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; -import com.microsoft.azure.sdk.iot.service.auth.IotHubServiceSasToken; import com.microsoft.azure.sdk.iot.service.methods.DirectMethodRequestOptions; +import com.microsoft.azure.sdk.iot.service.methods.DirectMethodResponse; import com.microsoft.azure.sdk.iot.service.methods.DirectMethodsClient; import com.microsoft.azure.sdk.iot.service.methods.DirectMethodsClientOptions; -import com.microsoft.azure.sdk.iot.service.methods.DirectMethodResponse; +import com.microsoft.azure.sdk.iot.service.registry.RegistryClient; +import com.microsoft.azure.sdk.iot.service.registry.RegistryClientOptions; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.runners.Parameterized; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.ClientType; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.IntegrationTest; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.TestConstants; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.TestIdentity; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.TestModuleIdentity; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.Tools; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.*; import java.nio.charset.StandardCharsets; import java.util.*; -import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; +import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.AMQPS; +import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.MQTT; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; -import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SELF_SIGNED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -61,7 +52,15 @@ public static Collection inputs() iotHubConnectionString = Tools.retrieveEnvironmentVariableValue(TestConstants.IOT_HUB_CONNECTION_STRING_ENV_VAR_NAME); isBasicTierHub = Boolean.parseBoolean(Tools.retrieveEnvironmentVariableValue(TestConstants.IS_BASIC_TIER_HUB_ENV_VAR_NAME)); isPullRequest = Boolean.parseBoolean(Tools.retrieveEnvironmentVariableValue(TestConstants.IS_PULL_REQUEST)); - return inputsCommon(); + + return Arrays.asList( + new Object[][] + { + {AMQPS, SAS, ClientType.DEVICE_CLIENT}, + {MQTT, SAS, ClientType.DEVICE_CLIENT}, + {AMQPS, SAS, ClientType.MODULE_CLIENT}, + {MQTT, SAS, ClientType.MODULE_CLIENT}, + }); } protected static String iotHubConnectionString = ""; @@ -72,46 +71,6 @@ public static Collection inputs() protected DirectMethodTestInstance testInstance; - protected static Collection inputsCommon() - { - Collection inputs = new ArrayList<>(); - - for (ClientType clientType : ClientType.values()) - { - for (IotHubClientProtocol protocol : IotHubClientProtocol.values()) - { - if (protocol != HTTPS) - { - for (AuthenticationType authenticationType : AuthenticationType.values()) - { - if (authenticationType == SAS) - { - inputs.add(makeSubArray(protocol, authenticationType, clientType)); - } - else if (authenticationType == SELF_SIGNED) - { - if (protocol != AMQPS_WS && protocol != MQTT_WS) - { - inputs.add(makeSubArray(protocol, authenticationType, clientType)); - } - } - } - } - } - } - - return inputs; - } - - private static Object[] makeSubArray(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) - { - Object[] inputSubArray = new Object[3]; - inputSubArray[0] = protocol; - inputSubArray[1] = authenticationType; - inputSubArray[2] = clientType; - return inputSubArray; - } - protected DirectMethodsCommon(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) throws Exception { this.testInstance = new DirectMethodTestInstance(protocol, authenticationType, clientType); @@ -474,13 +433,4 @@ private void assertPayloadHelper(Object senderPayload, Object receiverPayload) assertEquals(senderPayload, receiverPayload); } } - - protected static DirectMethodsClient buildDeviceMethodClientWithAzureSasCredential() - { - IotHubConnectionString iotHubConnectionStringObj = IotHubConnectionStringBuilder.createIotHubConnectionString(iotHubConnectionString); - IotHubServiceSasToken serviceSasToken = new IotHubServiceSasToken(iotHubConnectionStringObj); - AzureSasCredential azureSasCredential = new AzureSasCredential(serviceSasToken.toString()); - DirectMethodsClientOptions options = DirectMethodsClientOptions.builder().httpReadTimeoutSeconds(HTTP_READ_TIMEOUT).build(); - return new DirectMethodsClient(iotHubConnectionStringObj.getHostName(), azureSasCredential, options); - } } diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/ReceiveMessagesCommon.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/ReceiveMessagesCommon.java index 5d8482cc94..a6dc015100 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/ReceiveMessagesCommon.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/ReceiveMessagesCommon.java @@ -6,30 +6,32 @@ package tests.integration.com.microsoft.azure.sdk.iot.iothub.setup; -import com.microsoft.azure.sdk.iot.device.*; -import com.microsoft.azure.sdk.iot.service.messaging.DeliveryAcknowledgement; -import com.microsoft.azure.sdk.iot.service.messaging.Message; +import com.microsoft.azure.sdk.iot.device.DeviceClient; +import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol; +import com.microsoft.azure.sdk.iot.device.IotHubMessageResult; +import com.microsoft.azure.sdk.iot.device.ModuleClient; import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException; -import com.microsoft.azure.sdk.iot.service.messaging.IotHubServiceClientProtocol; -import com.microsoft.azure.sdk.iot.service.messaging.MessagingClient; +import com.microsoft.azure.sdk.iot.service.messaging.*; import com.microsoft.azure.sdk.iot.service.registry.RegistryClient; import com.microsoft.azure.sdk.iot.service.registry.RegistryClientOptions; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.junit.After; -import org.junit.Assert; import org.junit.runners.Parameterized; +import tests.integration.com.microsoft.azure.sdk.iot.helpers.Tools; import tests.integration.com.microsoft.azure.sdk.iot.helpers.*; import tests.integration.com.microsoft.azure.sdk.iot.iothub.telemetry.ReceiveMessagesTests; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeoutException; +import java.util.function.Function; import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; -import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SELF_SIGNED; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static tests.integration.com.microsoft.azure.sdk.iot.helpers.CorrelationDetailsLoggingAssert.buildExceptionMessage; /** @@ -51,41 +53,15 @@ public static Collection inputs() messageProperties.put("name2", "value2"); messageProperties.put("name3", "value3"); - List inputs = new ArrayList(Arrays.asList( - new Object[][] - { - //sas token module client - {MQTT, SAS, ClientType.DEVICE_CLIENT}, - {AMQPS, SAS, ClientType.DEVICE_CLIENT}, - {MQTT_WS, SAS, ClientType.DEVICE_CLIENT}, - {AMQPS_WS, SAS, ClientType.DEVICE_CLIENT}, - - //x509 module client - {HTTPS, SELF_SIGNED, ClientType.DEVICE_CLIENT}, - {MQTT, SELF_SIGNED, ClientType.DEVICE_CLIENT}, - {AMQPS, SELF_SIGNED, ClientType.DEVICE_CLIENT} - } - )); - - if (!IntegrationTest.isBasicTierHub) - { - inputs.addAll(Arrays.asList( - new Object[][] - { - //sas token module client - {MQTT, SAS, ClientType.MODULE_CLIENT}, - {AMQPS, SAS, ClientType.MODULE_CLIENT}, - {MQTT_WS, SAS, ClientType.MODULE_CLIENT}, - {AMQPS_WS, SAS, ClientType.MODULE_CLIENT}, - - //x509 module client - {MQTT, SELF_SIGNED, ClientType.MODULE_CLIENT}, - {AMQPS, SELF_SIGNED, ClientType.MODULE_CLIENT} - } - )); - } - - return inputs; + return Arrays.asList( + new Object[][] + { + {HTTPS, SAS, ClientType.DEVICE_CLIENT}, + {AMQPS, SAS, ClientType.DEVICE_CLIENT}, + {MQTT, SAS, ClientType.DEVICE_CLIENT}, + {AMQPS, SAS, ClientType.MODULE_CLIENT}, + {MQTT, SAS, ClientType.MODULE_CLIENT}, + }); } protected static Map messageProperties = new HashMap<>(3); @@ -100,8 +76,6 @@ public static Collection inputs() protected static final int FEEDBACK_TIMEOUT_MILLIS = 60 * 1000; // 1 minute - protected static final long ERROR_INJECTION_RECOVERY_TIMEOUT_MILLISECONDS = 60 * 1000; // 1 minute - public ReceiveMessagesTestInstance testInstance; public ReceiveMessagesCommon(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) throws Exception @@ -235,59 +209,60 @@ public IotHubMessageResult onCloudToDeviceMessageReceived(com.microsoft.azure.sd } } - public static class MessageCallbackMqtt implements com.microsoft.azure.sdk.iot.device.MessageCallback + protected static boolean hasExpectedProperties(com.microsoft.azure.sdk.iot.device.Message msg, Map messageProperties) { - private final Message expectedMessage; - - public MessageCallbackMqtt() + for (String key : messageProperties.keySet()) { - this.expectedMessage = null; + if (msg.getProperty(key) == null || !msg.getProperty(key).equals(messageProperties.get(key))) + { + return false; + } } - public MessageCallbackMqtt(Message expectedMessage) - { - this.expectedMessage = expectedMessage; - } + return true; + } - public IotHubMessageResult onCloudToDeviceMessageReceived(com.microsoft.azure.sdk.iot.device.Message msg, Object context) + public void receiveMessage(int messageSize, boolean withOpenClose) throws Exception + { + if (withOpenClose) { - HashMap messageProperties = (HashMap) ReceiveMessagesTests.messageProperties; - Success messageReceived = (Success)context; - boolean resultValue = true; + testInstance.identity.getClient().open(false); + } - if (this.expectedMessage != null) - { - if (!hasExpectedProperties(msg, messageProperties) || !hasExpectedSystemProperties(msg, expectedMessage.getCorrelationId(), expectedMessage.getMessageId())) - { - log.warn("Unexpected properties in the received message"); - resultValue = false; - } + Message serviceMessage = createCloudToDeviceMessage(messageSize); + serviceMessage.setMessageId(UUID.randomUUID().toString()); - if (!ArrayUtils.isEquals(this.expectedMessage.getBytes(), msg.getBytes())) - { - log.warn("Unexpected payload in the received message"); - resultValue = false; - } - } + MessageCallback callback = new MessageCallback(serviceMessage); - messageReceived.callbackWasFired(); - messageReceived.setResult(resultValue); + Success messageReceived = new Success(); - return IotHubMessageResult.COMPLETE; + if (testInstance.identity.getClient() instanceof DeviceClient) + { + ((DeviceClient) testInstance.identity.getClient()).setMessageCallback(callback, messageReceived); + } + else if (testInstance.identity.getClient() instanceof ModuleClient) + { + ((ModuleClient) testInstance.identity.getClient()).setMessageCallback(callback, messageReceived); } - } - protected static boolean hasExpectedProperties(com.microsoft.azure.sdk.iot.device.Message msg, Map messageProperties) - { - for (String key : messageProperties.keySet()) + if (testInstance.identity.getClient() instanceof DeviceClient) { - if (msg.getProperty(key) == null || !msg.getProperty(key).equals(messageProperties.get(key))) - { - return false; - } + testInstance.messagingClient.send(testInstance.identity.getDeviceId(), serviceMessage); + } + else if (testInstance.identity.getClient() instanceof ModuleClient) + { + testInstance.messagingClient.send(testInstance.identity.getDeviceId(), ((TestModuleIdentity) testInstance.identity).getModuleId(), serviceMessage); } - return true; + waitForMessageToBeReceived(messageReceived, testInstance.protocol.toString()); + + // flakey feature + //waitForFeedbackMessage(serviceMessage.getMessageId()); + + if (withOpenClose) + { + testInstance.identity.getClient().close(); + } } protected static boolean hasExpectedSystemProperties(com.microsoft.azure.sdk.iot.device.Message msg, String expectedCorrelationId, String expectedMessageId) @@ -304,7 +279,7 @@ protected Message createCloudToDeviceMessage(int messageSize) throws IotHubExcep { byte[] payload = new byte[messageSize]; new Random().nextBytes(payload); - com.microsoft.azure.sdk.iot.service.messaging.Message serviceMessage = new Message(payload); + Message serviceMessage = new Message(payload); serviceMessage.setCorrelationId(UUID.randomUUID().toString()); serviceMessage.setMessageId(UUID.randomUUID().toString()); serviceMessage.setProperties(messageProperties); @@ -312,16 +287,6 @@ protected Message createCloudToDeviceMessage(int messageSize) throws IotHubExcep return serviceMessage; } - protected void sendMessageToDevice(String deviceId, int messageSize) throws IotHubException, IOException, InterruptedException, TimeoutException - { - testInstance.messagingClient.send(deviceId, createCloudToDeviceMessage(messageSize)); - } - - protected void sendMessageToModule(String deviceId, String moduleId, int messageSize) throws IotHubException, IOException, InterruptedException, TimeoutException - { - testInstance.messagingClient.send(deviceId, moduleId, createCloudToDeviceMessage(messageSize)); - } - protected void waitForMessageToBeReceived(Success messageReceived, String protocolName) { try @@ -333,18 +298,61 @@ protected void waitForMessageToBeReceived(Success messageReceived, String protoc if (System.currentTimeMillis() - startTime > RECEIVE_TIMEOUT_MILLISECONDS) { - Assert.fail(buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Timed out waiting to receive message", testInstance.identity.getClient())); + fail(buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Timed out waiting to receive message", testInstance.identity.getClient())); } } if (!messageReceived.getResult()) { - Assert.fail(buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Receiving message over " + protocolName + " protocol failed. Received message was missing expected properties", testInstance.identity.getClient())); + fail(buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Receiving message over " + protocolName + " protocol failed. Received message was missing expected properties", testInstance.identity.getClient())); } } catch (InterruptedException e) { - Assert.fail(buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Receiving message over " + protocolName + " protocol failed. Unexpected interrupted exception occurred", testInstance.identity.getClient())); + fail(buildExceptionMessage(testInstance.protocol + ", " + testInstance.authenticationType + ": Receiving message over " + protocolName + " protocol failed. Unexpected interrupted exception occurred", testInstance.identity.getClient())); } } + + private void waitForFeedbackMessage(String expectedMessageId) throws InterruptedException, IOException, IotHubException, TimeoutException + { + final Success feedbackReceived = new Success(); + + Function feedbackMessageProcessor = feedbackBatch -> + { + for (FeedbackRecord feedbackRecord : feedbackBatch.getRecords()) + { + if (feedbackRecord.getDeviceId().equals(testInstance.identity.getDeviceId()) + && feedbackRecord.getOriginalMessageId().equals(expectedMessageId)) + { + feedbackReceived.setResult(true); + feedbackReceived.callbackWasFired(); + } + } + + return AcknowledgementType.ABANDON; + }; + + MessageFeedbackProcessorClientOptions messageFeedbackProcessorClientOptions = + MessageFeedbackProcessorClientOptions.builder() + .build(); + + MessageFeedbackProcessorClient messageFeedbackProcessorClient = + new MessageFeedbackProcessorClient(iotHubConnectionString, IotHubServiceClientProtocol.AMQPS_WS, feedbackMessageProcessor, messageFeedbackProcessorClientOptions); + + messageFeedbackProcessorClient.start(); + + long startTime = System.currentTimeMillis(); + while (!feedbackReceived.wasCallbackFired()) + { + Thread.sleep(1000); + + if (System.currentTimeMillis() - startTime > FEEDBACK_TIMEOUT_MILLIS) + { + fail("Timed out waiting on notification for device " + testInstance.identity.getDeviceId()); + } + } + + messageFeedbackProcessorClient.stop(); + assertTrue(feedbackReceived.getResult()); + } } diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/SendMessagesCommon.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/SendMessagesCommon.java index 0ddf65d2e3..d124b9ad10 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/SendMessagesCommon.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/SendMessagesCommon.java @@ -53,7 +53,7 @@ @Slf4j public class SendMessagesCommon extends IntegrationTest { - @Parameterized.Parameters(name = "{0}_{1}_{2}_{3}") + @Parameterized.Parameters(name = "{0}_{1}_{2}") public static Collection inputs() throws Exception { iotHubConnectionString = Tools.retrieveEnvironmentVariableValue(TestConstants.IOT_HUB_CONNECTION_STRING_ENV_VAR_NAME); @@ -63,108 +63,34 @@ public static Collection inputs() throws Exception registryClient = new RegistryClient(iotHubConnectionString, RegistryClientOptions.builder().httpReadTimeoutSeconds(HTTP_READ_TIMEOUT).build()); hostName = IotHubConnectionStringBuilder.createIotHubConnectionString(iotHubConnectionString).getHostName(); - List inputs = new ArrayList(Arrays.asList( - new Object[][] - { - //sas token device client, no proxy - {HTTPS, SAS, ClientType.DEVICE_CLIENT, false}, - {MQTT, SAS, ClientType.DEVICE_CLIENT, false}, - {AMQPS, SAS, ClientType.DEVICE_CLIENT, false}, - {MQTT_WS, SAS, ClientType.DEVICE_CLIENT, false}, - {AMQPS_WS, SAS, ClientType.DEVICE_CLIENT, false}, - - //x509 device client, no proxy - {HTTPS, SELF_SIGNED, ClientType.DEVICE_CLIENT, false}, - {MQTT, SELF_SIGNED, ClientType.DEVICE_CLIENT, false}, - {AMQPS, SELF_SIGNED, ClientType.DEVICE_CLIENT, false}, - - //sas token device client, with proxy - {MQTT_WS, SAS, ClientType.DEVICE_CLIENT, true}, - {AMQPS_WS, SAS, ClientType.DEVICE_CLIENT, true}, - - //x509 device client, with proxy - {HTTPS, SELF_SIGNED, ClientType.DEVICE_CLIENT, true} - } - )); - - if (!isBasicTierHub) - { - inputs.addAll(Arrays.asList( - new Object[][] - { - //sas token module client, no proxy - {MQTT, SAS, ClientType.MODULE_CLIENT, false}, - {AMQPS, SAS, ClientType.MODULE_CLIENT, false}, - {MQTT_WS, SAS, ClientType.MODULE_CLIENT, false}, - {AMQPS_WS, SAS, ClientType.MODULE_CLIENT, false}, - - //x509 module client, no proxy - {MQTT, SELF_SIGNED, ClientType.MODULE_CLIENT, false}, - {AMQPS, SELF_SIGNED, ClientType.MODULE_CLIENT, false}, - - //sas token module client, with proxy - {MQTT_WS, SAS, ClientType.MODULE_CLIENT, true}, - {AMQPS_WS, SAS, ClientType.MODULE_CLIENT, true} - } - )); - } - - return inputs; + return Arrays.asList( + new Object[][] + { + {HTTPS, SAS, ClientType.DEVICE_CLIENT}, + {AMQPS, SAS, ClientType.DEVICE_CLIENT}, + {MQTT, SAS, ClientType.DEVICE_CLIENT}, + {AMQPS, SAS, ClientType.MODULE_CLIENT}, + {MQTT, SAS, ClientType.MODULE_CLIENT}, + }); } - protected static final Integer NUM_SMALL_MESSAGES = 50; - // Max IoT Hub message size is 256 kb, but that includes headers, not just payload protected static final int MAX_MESSAGE_PAYLOAD_SIZE = 255*1024; // How much to wait until a message makes it to the server, in milliseconds protected static final Integer SEND_TIMEOUT_MILLISECONDS = 60 * 1000; - //How many milliseconds between retry - protected static final Integer RETRY_MILLISECONDS = 100; - protected static String iotHubConnectionString = ""; protected static String hostName; - //The messages to be sent in these tests. Some contain error injection messages surrounded by normal messages - protected List NORMAL_MESSAGES_TO_SEND = new ArrayList<>(); - protected List LARGE_MESSAGES_TO_SEND = new ArrayList<>(); - protected List MULTIPLE_SMALL_MESSAGES_TO_SEND = new ArrayList<>(); - protected List TCP_CONNECTION_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_CONNECTION_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_SESSION_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_CBS_REQUEST_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_CBS_RESPONSE_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_C2D_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_D2C_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_METHOD_REQ_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_METHOD_RESP_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_TWIN_REQ_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_TWIN_RESP_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - protected List AMQP_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND = new ArrayList<>(); - protected List MQTT_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND = new ArrayList<>(); - public SendMessagesTestInstance testInstance; - //How much messages each device will send to the hub for each connection. - protected static final Integer NUM_MESSAGES_PER_CONNECTION = 6; - protected static RegistryClient registryClient; - protected static HttpProxyServer proxyServer; - protected static String testProxyHostname = "127.0.0.1"; - protected static int testProxyPort = 8899; - - // Semmle flags this as a security issue, but this is a test username so the warning can be suppressed - protected static final String testProxyUser = "proxyUsername"; // lgtm - // Semmle flags this as a security issue, but this is a test password so the warning can be suppressed - protected static final char[] testProxyPass = "1234".toCharArray(); // lgtm - - - public SendMessagesCommon(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean withProxy) + public SendMessagesCommon(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) { - this.testInstance = new SendMessagesTestInstance(protocol, authenticationType, clientType, withProxy); + this.testInstance = new SendMessagesTestInstance(protocol, authenticationType, clientType); } @BeforeClass @@ -173,21 +99,6 @@ public static void classSetup() registryClient = new RegistryClient(iotHubConnectionString, RegistryClientOptions.builder().httpReadTimeoutSeconds(HTTP_READ_TIMEOUT).build()); } - @BeforeClass - public static void startProxy() - { - proxyServer = DefaultHttpProxyServer.bootstrap() - .withPort(testProxyPort) - .withProxyAuthenticator(new BasicProxyAuthenticator(testProxyUser, testProxyPass)) - .start(); - } - - @AfterClass - public static void stopProxy() - { - proxyServer.stop(); - } - public class SendMessagesTestInstance { public IotHubClientProtocol protocol; @@ -195,15 +106,13 @@ public class SendMessagesTestInstance public AuthenticationType authenticationType; public ClientType clientType; public String x509Thumbprint; - public boolean useHttpProxy; - public SendMessagesTestInstance(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean useHttpProxy) + public SendMessagesTestInstance(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) { this.protocol = protocol; this.authenticationType = authenticationType; this.clientType = clientType; this.x509Thumbprint = x509CertificateGenerator.getX509Thumbprint(); - this.useHttpProxy = useHttpProxy; } public void setup() throws Exception @@ -232,11 +141,6 @@ public void setup(boolean useCustomSasTokenProvider) throws Exception { public void setup(SSLContext customSSLContext, boolean useCustomSasTokenProvider) throws Exception { ClientOptions.ClientOptionsBuilder optionsBuilder = ClientOptions.builder(); - if (this.useHttpProxy) - { - Proxy testProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(testProxyHostname, testProxyPort)); - optionsBuilder.proxySettings(new ProxySettings(testProxy, testProxyUser, testProxyPass)); - } if (clientType == ClientType.DEVICE_CLIENT) { @@ -259,8 +163,6 @@ else if (clientType == ClientType.MODULE_CLIENT) { this.identity = Tools.getTestModule(iotHubConnectionString, this.protocol, this.authenticationType , false, optionsBuilder); } - - buildMessageLists(); } public void dispose() @@ -274,204 +176,6 @@ public void dispose() } } - public static class testDevice implements Runnable - { - public DeviceClient client; - public String messageString; - public String connString; - - public IotHubClientProtocol protocol; - public Integer numMessagesPerConnection; - public Integer numConnectionsPerDevice; - public Integer sendTimeout; - public Integer numKeys; - public CountDownLatch latch; - public AtomicBoolean succeed; - - @Override - public void run() - { - for (int i = 0; i < this.numConnectionsPerDevice; i++) - { - try - { - this.openConnection(); - this.sendMessages(); - this.closeConnection(); - } - catch (Exception | AssertionError x) - { - succeed.set(false); - System.out.print("testDevice thread: " + x.getMessage()); - } - } - latch.countDown(); - } - - public testDevice(Device deviceAmqps, IotHubClientProtocol protocol, - Integer numConnectionsPerDevice, Integer numMessagesPerConnection, - Integer numKeys, Integer sendTimeout, CountDownLatch latch, AtomicBoolean succeed) - { - this.protocol = protocol; - this.numConnectionsPerDevice = numConnectionsPerDevice; - this.numMessagesPerConnection = numMessagesPerConnection; - this.sendTimeout = sendTimeout; - this.numKeys = numKeys; - this.latch = latch; - - this.succeed = succeed; - this.succeed.set(true); - - this.connString = DeviceConnectionString.get(iotHubConnectionString, deviceAmqps); - - messageString = "Java client " + deviceAmqps.getDeviceId() + " test e2e message over AMQP protocol"; - } - - public void openConnection() throws IOException, URISyntaxException, IotHubClientException - { - client = new DeviceClient(connString, protocol); - client.open(false); - } - - public void sendMessages() - { - for (int i = 0; i < numMessagesPerConnection; ++i) - { - try - { - Message msgSend = new Message(messageString); - msgSend.setProperty("messageCount", Integer.toString(i)); - for (int j = 0; j < numKeys; j++) - { - msgSend.setProperty("key"+j, "value"+j); - } - - Success messageSent = new Success(); - EventCallback callback = new EventCallback(IotHubStatusCode.OK); - client.sendEventAsync(msgSend, callback, messageSent); - - long startTime = System.currentTimeMillis(); - while(!messageSent.wasCallbackFired()) - { - Thread.sleep(RETRY_MILLISECONDS); - if (System.currentTimeMillis() - startTime > sendTimeout) - { - Assert.fail("Timed out waiting for event callback"); - } - } - - if (messageSent.getCallbackStatusCode() != IotHubStatusCode.OK) - { - Assert.fail("Sending message over AMQPS protocol failed: expected OK but received " + messageSent.getCallbackStatusCode()); - } - } - catch (Exception e) - { - Assert.fail("Sending message over AMQPS protocol throws " + e); - } - } - } - - public void closeConnection() throws IOException - { - client.close(); - } - } - - /** - * Each error injection test will take a list of messages to send. In that list, there will be an error injection message in the middle - */ - protected void buildMessageLists() - { - NORMAL_MESSAGES_TO_SEND = new ArrayList<>(); - TCP_CONNECTION_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_CONNECTION_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_SESSION_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_CBS_REQUEST_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_CBS_RESPONSE_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_C2D_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_D2C_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_METHOD_REQ_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_METHOD_RESP_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_TWIN_REQ_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_TWIN_RESP_LINK_DROP_MESSAGES_TO_SEND = new ArrayList<>(); - AMQP_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND = new ArrayList<>(); - MQTT_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND = new ArrayList<>(); - LARGE_MESSAGES_TO_SEND = new ArrayList<>(); - MULTIPLE_SMALL_MESSAGES_TO_SEND = new ArrayList<>(); - - MessageAndResult normalMessageAndExpectedResult = new MessageAndResult(new Message("test message"), IotHubStatusCode.OK); - for (int i = 0; i < NUM_MESSAGES_PER_CONNECTION; i++) - { - //error injection should take place in the middle of normal communications - if (i == (NUM_MESSAGES_PER_CONNECTION / 2)) - { - //messages that tests should recover from - Message tcpConnectionDropErrorInjectionMessage = ErrorInjectionHelper.tcpConnectionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - TCP_CONNECTION_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(tcpConnectionDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpConnectionDropErrorInjectionMessage = ErrorInjectionHelper.amqpsConnectionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_CONNECTION_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpConnectionDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpSessionDropErrorInjectionMessage = ErrorInjectionHelper.amqpsSessionDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_SESSION_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpSessionDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpCbsRequestLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsCBSReqLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_CBS_REQUEST_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpCbsRequestLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpCbsResponseLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsCBSRespLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_CBS_RESPONSE_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpCbsResponseLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpC2DLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsC2DLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_C2D_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpC2DLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpD2CLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsD2CTelemetryLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_D2C_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpD2CLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpMethodReqLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsMethodReqLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_METHOD_REQ_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpMethodReqLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpMethodRespLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsMethodRespLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_METHOD_RESP_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpMethodRespLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpTwinReqLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsTwinReqLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_TWIN_REQ_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpTwinReqLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpTwinRespLinkDropErrorInjectionMessage = ErrorInjectionHelper.amqpsTwinRespLinkDropErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_TWIN_RESP_LINK_DROP_MESSAGES_TO_SEND.add(new MessageAndResult(amqpTwinRespLinkDropErrorInjectionMessage, IotHubStatusCode.OK)); - - Message amqpGracefulShutdownErrorInjectionMessage = ErrorInjectionHelper.amqpsGracefulShutdownErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - AMQP_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND.add(new MessageAndResult(amqpGracefulShutdownErrorInjectionMessage, IotHubStatusCode.OK)); - - Message mqttGracefulShutdownErrorInjectionMessage = ErrorInjectionHelper.mqttGracefulShutdownErrorInjectionMessage(ErrorInjectionHelper.DefaultDelayInSec, ErrorInjectionHelper.DefaultDurationInSec); - MQTT_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND.add(new MessageAndResult(mqttGracefulShutdownErrorInjectionMessage, IotHubStatusCode.OK)); - } - else - { - TCP_CONNECTION_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_CONNECTION_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_SESSION_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_CBS_REQUEST_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_CBS_RESPONSE_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_C2D_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_D2C_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_METHOD_REQ_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_METHOD_RESP_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_TWIN_REQ_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_TWIN_RESP_LINK_DROP_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - AMQP_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - MQTT_GRACEFUL_SHUTDOWN_MESSAGES_TO_SEND.add(normalMessageAndExpectedResult); - } - - NORMAL_MESSAGES_TO_SEND.add(new MessageAndResult(new Message("test message" + UUID.randomUUID() ), IotHubStatusCode.OK)); - LARGE_MESSAGES_TO_SEND.add(new MessageAndResult(new Message(new byte[MAX_MESSAGE_PAYLOAD_SIZE]), IotHubStatusCode.OK)); - } - - for (int i = 0 ; i < NUM_SMALL_MESSAGES; i++){ - MULTIPLE_SMALL_MESSAGES_TO_SEND.add(new MessageAndResult(new Message("test message" + UUID.randomUUID() ), IotHubStatusCode.OK)); - } - } - @After public void tearDownTest() { diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/TwinCommon.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/TwinCommon.java index 96f2e1d797..9dfb9451da 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/TwinCommon.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/setup/TwinCommon.java @@ -56,42 +56,14 @@ public static Collection inputs() IntegrationTest.isBasicTierHub = Boolean.parseBoolean(Tools.retrieveEnvironmentVariableValue(TestConstants.IS_BASIC_TIER_HUB_ENV_VAR_NAME)); IntegrationTest.isPullRequest = Boolean.parseBoolean(Tools.retrieveEnvironmentVariableValue(TestConstants.IS_PULL_REQUEST)); - List inputs = new ArrayList(); - for (ClientType clientType : ClientType.values()) - { - if (clientType == ClientType.DEVICE_CLIENT) - { - inputs.addAll(Arrays.asList( - new Object[][] - { - //sas token, device client - {AMQPS, SAS, ClientType.DEVICE_CLIENT}, - {AMQPS_WS, SAS, ClientType.DEVICE_CLIENT}, - {MQTT, SAS, ClientType.DEVICE_CLIENT}, - {MQTT_WS, SAS, ClientType.DEVICE_CLIENT}, - - //x509, device client - {AMQPS, SELF_SIGNED, ClientType.DEVICE_CLIENT}, - {MQTT, SELF_SIGNED, ClientType.DEVICE_CLIENT}, - } - )); - } - else - { - inputs.addAll(Arrays.asList( - new Object[][] - { - //sas token, module client - {AMQPS, SAS, ClientType.MODULE_CLIENT}, - {AMQPS_WS, SAS, ClientType.MODULE_CLIENT}, - {MQTT, SAS, ClientType.MODULE_CLIENT}, - {MQTT_WS, SAS, ClientType.MODULE_CLIENT} - } - )); - } - } - - return inputs; + return Arrays.asList( + new Object[][] + { + {AMQPS, SAS, ClientType.DEVICE_CLIENT}, + {MQTT, SAS, ClientType.DEVICE_CLIENT}, + {AMQPS, SAS, ClientType.MODULE_CLIENT}, + {MQTT, SAS, ClientType.MODULE_CLIENT}, + }); } // Max time to wait to see it on Hub diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/ReceiveMessagesTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/ReceiveMessagesTests.java index 099bc8e34f..bce29f6cf1 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/ReceiveMessagesTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/ReceiveMessagesTests.java @@ -6,39 +6,18 @@ package tests.integration.com.microsoft.azure.sdk.iot.iothub.telemetry; -import com.microsoft.azure.sdk.iot.device.DeviceClient; import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol; -import com.microsoft.azure.sdk.iot.device.ModuleClient; -import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException; -import com.microsoft.azure.sdk.iot.service.messaging.AcknowledgementType; -import com.microsoft.azure.sdk.iot.service.messaging.FeedbackBatch; -import com.microsoft.azure.sdk.iot.service.messaging.FeedbackRecord; -import com.microsoft.azure.sdk.iot.service.messaging.IotHubServiceClientProtocol; -import com.microsoft.azure.sdk.iot.service.messaging.Message; import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; -import com.microsoft.azure.sdk.iot.service.messaging.MessageFeedbackProcessorClient; -import com.microsoft.azure.sdk.iot.service.messaging.MessageFeedbackProcessorClientOptions; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import tests.integration.com.microsoft.azure.sdk.iot.helpers.ClientType; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.Success; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.TestModuleIdentity; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.ContinuousIntegrationTest; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.IotHubTest; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.StandardTierHubOnlyTest; import tests.integration.com.microsoft.azure.sdk.iot.iothub.setup.ReceiveMessagesCommon; -import java.io.IOException; -import java.util.UUID; -import java.util.concurrent.TimeoutException; -import java.util.function.Function; - -import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; -import static junit.framework.TestCase.assertTrue; -import static junit.framework.TestCase.fail; - /** * Test class containing all non error injection tests to be run on JVM and android pertaining to receiving messages on a device/module. */ @@ -62,7 +41,7 @@ public void setupTest() throws Exception @StandardTierHubOnlyTest public void receiveMessage() throws Exception { - receiveMessage(MESSAGE_SIZE_IN_BYTES); + receiveMessage(MESSAGE_SIZE_IN_BYTES, true); } // Test out receiving a near-maximum sized cloud to device message both for testing the sending of it from the @@ -73,92 +52,6 @@ public void receiveMessage() throws Exception @StandardTierHubOnlyTest public void receiveLargeMessage() throws Exception { - receiveMessage(LARGE_MESSAGE_SIZE_IN_BYTES); - } - - public void receiveMessage(int messageSize) throws Exception - { - testInstance.identity.getClient().open(false); - - Message serviceMessage = createCloudToDeviceMessage(messageSize); - serviceMessage.setMessageId(UUID.randomUUID().toString()); - - com.microsoft.azure.sdk.iot.device.MessageCallback callback = new MessageCallback(serviceMessage); - - if (testInstance.protocol == MQTT || testInstance.protocol == MQTT_WS) - { - callback = new MessageCallbackMqtt(serviceMessage); - } - - Success messageReceived = new Success(); - - if (testInstance.identity.getClient() instanceof DeviceClient) - { - ((DeviceClient) testInstance.identity.getClient()).setMessageCallback(callback, messageReceived); - } - else if (testInstance.identity.getClient() instanceof ModuleClient) - { - ((ModuleClient) testInstance.identity.getClient()).setMessageCallback(callback, messageReceived); - } - - if (testInstance.identity.getClient() instanceof DeviceClient) - { - testInstance.messagingClient.send(testInstance.identity.getDeviceId(), serviceMessage); - } - else if (testInstance.identity.getClient() instanceof ModuleClient) - { - testInstance.messagingClient.send(testInstance.identity.getDeviceId(), ((TestModuleIdentity) testInstance.identity).getModuleId(), serviceMessage); - } - - waitForMessageToBeReceived(messageReceived, testInstance.protocol.toString()); - - // flakey feature - //waitForFeedbackMessage(serviceMessage.getMessageId()); - - Thread.sleep(200); - testInstance.identity.getClient().close(); - } - - private void waitForFeedbackMessage(String expectedMessageId) throws InterruptedException, IOException, IotHubException, TimeoutException - { - final Success feedbackReceived = new Success(); - - Function feedbackMessageProcessor = feedbackBatch -> - { - for (FeedbackRecord feedbackRecord : feedbackBatch.getRecords()) - { - if (feedbackRecord.getDeviceId().equals(testInstance.identity.getDeviceId()) - && feedbackRecord.getOriginalMessageId().equals(expectedMessageId)) - { - feedbackReceived.setResult(true); - feedbackReceived.callbackWasFired(); - } - } - - return AcknowledgementType.ABANDON; - }; - - MessageFeedbackProcessorClientOptions messageFeedbackProcessorClientOptions = - MessageFeedbackProcessorClientOptions.builder() - .build(); - - MessageFeedbackProcessorClient messageFeedbackProcessorClient = - new MessageFeedbackProcessorClient(iotHubConnectionString, IotHubServiceClientProtocol.AMQPS_WS, feedbackMessageProcessor, messageFeedbackProcessorClientOptions); - - messageFeedbackProcessorClient.start(); - - long startTime = System.currentTimeMillis(); - while (!feedbackReceived.wasCallbackFired()) - { - Thread.sleep(1000); - - if (System.currentTimeMillis() - startTime > FEEDBACK_TIMEOUT_MILLIS) - { - fail("Timed out waiting on notification for device " + testInstance.identity.getDeviceId()); - } - } - - messageFeedbackProcessorClient.stop(); - assertTrue(feedbackReceived.getResult()); + receiveMessage(LARGE_MESSAGE_SIZE_IN_BYTES, true); } } diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/SendMessagesTests.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/SendMessagesTests.java index 6712b80f23..31d004ac78 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/SendMessagesTests.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/iothub/telemetry/SendMessagesTests.java @@ -6,43 +6,31 @@ package tests.integration.com.microsoft.azure.sdk.iot.iothub.telemetry; -import com.microsoft.azure.sdk.iot.device.ClientOptions; -import com.microsoft.azure.sdk.iot.device.DeviceClient; import com.microsoft.azure.sdk.iot.device.IotHubClientProtocol; import com.microsoft.azure.sdk.iot.device.IotHubStatusCode; import com.microsoft.azure.sdk.iot.device.Message; import com.microsoft.azure.sdk.iot.device.exceptions.IotHubClientException; -import com.microsoft.azure.sdk.iot.service.registry.Device; -import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; import com.microsoft.azure.sdk.iot.device.transport.IotHubConnectionStatus; -import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException; +import com.microsoft.azure.sdk.iot.service.auth.AuthenticationType; import lombok.extern.slf4j.Slf4j; -import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import tests.integration.com.microsoft.azure.sdk.iot.helpers.ClientType; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.IotHubServicesCommon; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.MessageAndResult; import tests.integration.com.microsoft.azure.sdk.iot.helpers.SSLContextBuilder; import tests.integration.com.microsoft.azure.sdk.iot.helpers.Success; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.Tools; -import tests.integration.com.microsoft.azure.sdk.iot.helpers.X509CertificateGenerator; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.ContinuousIntegrationTest; import tests.integration.com.microsoft.azure.sdk.iot.helpers.annotations.IotHubTest; import tests.integration.com.microsoft.azure.sdk.iot.iothub.setup.SendMessagesCommon; -import javax.net.ssl.SSLContext; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.security.GeneralSecurityException; -import java.util.UUID; +import java.util.ArrayList; +import java.util.List; import static com.microsoft.azure.sdk.iot.device.IotHubClientProtocol.*; import static com.microsoft.azure.sdk.iot.service.auth.AuthenticationType.SAS; import static junit.framework.TestCase.*; import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; /** * Test class containing all non error injection tests to be run on JVM and android pertaining to sending messages from a device/module. @@ -52,17 +40,18 @@ @RunWith(Parameterized.class) public class SendMessagesTests extends SendMessagesCommon { - public SendMessagesTests(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType, boolean withProxy) throws Exception + public SendMessagesTests(IotHubClientProtocol protocol, AuthenticationType authenticationType, ClientType clientType) throws Exception { - super(protocol, authenticationType, clientType, withProxy); + super(protocol, authenticationType, clientType); } @Test public void sendMessages() throws Exception { this.testInstance.setup(); - - IotHubServicesCommon.sendMessages(testInstance.identity.getClient(), testInstance.protocol, NORMAL_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, 0, null); + testInstance.identity.getClient().open(true); + testInstance.identity.getClient().sendEvent(new Message("some message")); + testInstance.identity.getClient().close(); } // Ensure that a user can use a device/module client as soon as the connection status callback executes with status CONNECTED @@ -116,51 +105,58 @@ public void sendMessagesFromConnectionStatusChangeCallback() throws Exception testInstance.identity.getClient().close(); } - @Test - public void openClientWithRetry() throws Exception - { - this.testInstance.setup(); - this.testInstance.identity.getClient().open(true); - this.testInstance.identity.getClient().close(); - } - @Test public void sendMessagesWithCustomSasTokenProvider() throws Exception { - Assume.assumeTrue(testInstance.authenticationType == SAS); + assumeTrue(testInstance.authenticationType == SAS); this.testInstance.setup(true); - IotHubServicesCommon.sendMessages(testInstance.identity.getClient(), testInstance.protocol, NORMAL_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, 0, null); - } - - @Test - public void sendBulkMessages() throws Exception - { - this.testInstance.setup(); - - IotHubServicesCommon.sendBulkMessages(testInstance.identity.getClient(), testInstance.protocol, NORMAL_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, 0, null); + testInstance.identity.getClient().open(true); + testInstance.identity.getClient().sendEvent(new Message("Custom sas token provider client message")); + testInstance.identity.getClient().close(); } @Test @ContinuousIntegrationTest public void sendManySmallMessagesAsBatch() throws Exception { - // Only send batch messages in large quantities when using HTTPS protocol. - assumeFalse(this.testInstance.protocol != HTTPS); + // Only HTTP supports batching messages + assumeTrue(this.testInstance.protocol == HTTPS); this.testInstance.setup(); - IotHubServicesCommon.sendBulkMessages(testInstance.identity.getClient(), testInstance.protocol, MULTIPLE_SMALL_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, 0, null); + int count = 5; + List messages = new ArrayList<>(count); + for (int i = 0; i < count; i++) + { + messages.add(new Message("Bulk message " + i)); + } + + testInstance.identity.getClient().open(true); + testInstance.identity.getClient().sendEvents(messages); + testInstance.identity.getClient().close(); } @Test @ContinuousIntegrationTest public void sendLargestMessages() throws Exception { + // Only HTTP supports batching messages + assumeTrue(this.testInstance.protocol == HTTPS); + this.testInstance.setup(); - IotHubServicesCommon.sendMessages(testInstance.identity.getClient(), testInstance.protocol, LARGE_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, 0, null); + int count = 5; + List messages = new ArrayList<>(count); + for (int i = 0; i < count; i++) + { + messages.add(new Message(new byte[MAX_MESSAGE_PAYLOAD_SIZE])); + } + + testInstance.identity.getClient().open(true); + testInstance.identity.getClient().sendEvents(messages); + testInstance.identity.getClient().close(); } @Test @@ -173,8 +169,7 @@ public void sendMessagesWithUnusualApplicationProperties() throws Exception //All of these characters should be allowed within application properties msg.setProperty("TestKey1234!#$%&'*+-^_`|~", "TestValue1234!#$%&'*+-^_`|~()<>@,;:\\\"[]?={} \t"); - // ()<>@,;:\"[]?={} - IotHubServicesCommon.sendMessageAndWaitForResponse(this.testInstance.identity.getClient(), new MessageAndResult(msg, IotHubStatusCode.OK), testInstance.protocol); + this.testInstance.identity.getClient().sendEvent(msg); this.testInstance.identity.getClient().close(); } @@ -182,49 +177,33 @@ public void sendMessagesWithUnusualApplicationProperties() throws Exception @ContinuousIntegrationTest public void expiredMessagesAreNotSent() throws Exception { - // Not worth testing for both w/ and w/o proxy - assumeFalse(testInstance.useHttpProxy); - this.testInstance.setup(); - IotHubServicesCommon.sendExpiredMessageExpectingMessageExpiredCallback(testInstance.identity.getClient(), testInstance.protocol, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, testInstance.authenticationType); + this.testInstance.identity.getClient().open(true); + + try + { + Message expiredMessage = new Message("some pre-expired message"); + expiredMessage.setAbsoluteExpiryTime(1); + this.testInstance.identity.getClient().sendEvent(expiredMessage); + fail("Expected a MESSAGE_EXPIRED error but did not trigger one"); + } + catch (IotHubClientException e) + { + assertEquals(IotHubStatusCode.MESSAGE_EXPIRED, e.getStatusCode()); + } } @Test public void sendMessagesWithCustomSSLContextAndSasAuth() throws Exception { - //only testing sas based auth with custom ssl context here - assumeFalse(testInstance.authenticationType != SAS); + assumeTrue(testInstance.authenticationType == SAS); this.testInstance.setup(SSLContextBuilder.buildSSLContext()); - IotHubServicesCommon.sendMessages(testInstance.identity.getClient(), testInstance.protocol, NORMAL_MESSAGES_TO_SEND, RETRY_MILLISECONDS, SEND_TIMEOUT_MILLISECONDS, 0, null); - } - - @ContinuousIntegrationTest - @Test - public void sendMessagesWithECCCertificate() throws GeneralSecurityException, IOException, IotHubException, URISyntaxException, InterruptedException, IotHubClientException - { - // test is only applicable for self-signed device clients - assumeFalse(testInstance.authenticationType != AuthenticationType.SELF_SIGNED || testInstance.clientType != ClientType.DEVICE_CLIENT); - - // ECC cert generation is broken for Android. "ECDSA KeyPairGenerator is not available" - assumeFalse(Tools.isAndroid()); - - X509CertificateGenerator eccCertGenerator = - new X509CertificateGenerator(X509CertificateGenerator.CertificateAlgorithm.ECC); - - SSLContext sslContext = SSLContextBuilder.buildSSLContext(eccCertGenerator.getX509Certificate(), eccCertGenerator.getPrivateKey()); - - Device eccDevice = new Device(UUID.randomUUID().toString(), AuthenticationType.SELF_SIGNED); - eccDevice.setThumbprint(eccCertGenerator.getX509Thumbprint(), eccCertGenerator.getX509Thumbprint()); - eccDevice = registryClient.addDevice(eccDevice); - - ClientOptions clientOptions = ClientOptions.builder().sslContext(sslContext).build(); - DeviceClient deviceClient = new DeviceClient(Tools.getDeviceConnectionString(iotHubConnectionString, eccDevice), testInstance.protocol, clientOptions); - - deviceClient.open(false); - deviceClient.close(); + testInstance.identity.getClient().open(true); + testInstance.identity.getClient().sendEvent(new Message("some message")); + testInstance.identity.getClient().close(); } @Test diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java index 0790b22e0e..52cd28e971 100644 --- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java +++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java @@ -132,9 +132,7 @@ public static Collection inputsCommon(AttestationType attestationType) { {ProvisioningDeviceClientTransportProtocol.HTTPS, attestationType}, {ProvisioningDeviceClientTransportProtocol.AMQPS, attestationType}, - {ProvisioningDeviceClientTransportProtocol.AMQPS_WS, attestationType}, {ProvisioningDeviceClientTransportProtocol.MQTT, attestationType}, - {ProvisioningDeviceClientTransportProtocol.MQTT_WS, attestationType} }); } else if (attestationType == AttestationType.TPM) @@ -156,15 +154,11 @@ public static Collection inputsCommon() { {ProvisioningDeviceClientTransportProtocol.HTTPS, AttestationType.X509}, {ProvisioningDeviceClientTransportProtocol.AMQPS, AttestationType.X509}, - {ProvisioningDeviceClientTransportProtocol.AMQPS_WS, AttestationType.X509}, {ProvisioningDeviceClientTransportProtocol.MQTT, AttestationType.X509}, - {ProvisioningDeviceClientTransportProtocol.MQTT_WS, AttestationType.X509}, {ProvisioningDeviceClientTransportProtocol.HTTPS, AttestationType.SYMMETRIC_KEY}, {ProvisioningDeviceClientTransportProtocol.AMQPS, AttestationType.SYMMETRIC_KEY}, - {ProvisioningDeviceClientTransportProtocol.AMQPS_WS, AttestationType.SYMMETRIC_KEY}, {ProvisioningDeviceClientTransportProtocol.MQTT, AttestationType.SYMMETRIC_KEY}, - {ProvisioningDeviceClientTransportProtocol.MQTT_WS, AttestationType.SYMMETRIC_KEY} }); } diff --git a/iot-e2e-tests/iot-e2e-jvm-tests/pom.xml b/iot-e2e-tests/iot-e2e-jvm-tests/pom.xml index 9ff324ddfc..b445b53e5e 100644 --- a/iot-e2e-tests/iot-e2e-jvm-tests/pom.xml +++ b/iot-e2e-tests/iot-e2e-jvm-tests/pom.xml @@ -56,6 +56,18 @@ ${tpm-provider-emulator-artifact-id} ${tpm-provider-emulator-version} + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-slf4j-impl +