public async Task NoErrorFromCloudProxy_NoErrorDetailsReturned()
        {
            var batchSize = 10;

            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxy = ThrowingCloudProxy
                             .Create()
                             .WithBatchSize(batchSize)
                             .WithReportSuccessfulBatch(3)
                             .Build();

            Task <Option <ICloudProxy> > GetCloudProxy(string id) => Task.FromResult(Option.Some(cloudProxy));

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter, batchSize);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            var sinkResult = await cloudMessageProcessor.ProcessAsync(GetMessages("device1", 3 * batchSize), CancellationToken.None);

            Assert.True(sinkResult.IsSuccessful);
            Assert.Equal(3 * batchSize, sinkResult.Succeeded.Count);
            Assert.Equal(0, sinkResult.Failed.Count);
            Assert.Equal(0, sinkResult.InvalidDetailsList.Count);
            Assert.Equal(Option.None <SendFailureDetails>(), sinkResult.SendFailureDetails);
        }
        public async Task TransientErrorInFirstBatch_FastFailsRest()
        {
            var batchSize = 10;

            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxy = ThrowingCloudProxy
                             .Create()
                             .WithBatchSize(batchSize)
                             .WithReportErrorInBatch(ThrowingCloudProxy.Throw <Client.Exceptions.IotHubException>())
                             .WithReportSuccessfulBatch(2)
                             .Build();

            Task <Try <ICloudProxy> > GetCloudProxy(string id) => Task.FromResult(Try.Success(cloudProxy));

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter, true);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            var sinkResult = await cloudMessageProcessor.ProcessAsync(GetMessages("device1", 3 * batchSize), CancellationToken.None);

            // although the test is setup to succeed with batch 2 and 3, they will fast fail because of the first one
            Assert.False(sinkResult.IsSuccessful);
            Assert.Equal(0, sinkResult.Succeeded.Count);
            Assert.Equal(30, sinkResult.Failed.Count);
            Assert.Equal(0, sinkResult.InvalidDetailsList.Count);
            Assert.True(sinkResult.SendFailureDetails.HasValue);
            Assert.Equal(FailureKind.Transient, sinkResult.SendFailureDetails.Expect(() => new Exception()).FailureKind);
        }
Beispiel #3
0
        public void CloudEndpointInvokeChangeDetectionTest()
        {
            var handler = new RecordedDelegatingHandler {
                StatusCodeToReturn = HttpStatusCode.OK
            };

            using (MockContext context = MockContext.Start(this.GetType()))
            {
                IResourceManagementClient    resourcesClient             = StorageSyncManagementTestUtilities.GetResourceManagementClient(context, handler);
                IStorageSyncManagementClient storageSyncManagementClient = StorageSyncManagementTestUtilities.GetStorageSyncManagementClient(context, handler);

                // Create ResourceGroup
                string resourceGroupName = StorageSyncManagementTestUtilities.CreateResourceGroup(resourcesClient);

                // Create CloudEndpoint Name
                string storageSyncServiceName = TestUtilities.GenerateName("sss-cepchangedetection");
                string syncGroupName          = TestUtilities.GenerateName("sg-cepchangedetection");
                string cloudEndpointName      = TestUtilities.GenerateName("cepchangedetection");

                var storageSyncServiceParameters = StorageSyncManagementTestUtilities.GetDefaultStorageSyncServiceParameters();
                var syncGroupParameters          = StorageSyncManagementTestUtilities.GetDefaultSyncGroupParameters();
                var cloudEndpointParameters      = StorageSyncManagementTestUtilities.GetDefaultCloudEndpointParameters();

                StorageSyncService storageSyncServiceResource = storageSyncManagementClient.StorageSyncServices.Create(resourceGroupName, storageSyncServiceName, storageSyncServiceParameters);
                Assert.NotNull(storageSyncServiceResource);
                StorageSyncManagementTestUtilities.VerifyStorageSyncServiceProperties(storageSyncServiceResource, true);

                SyncGroup syncGroupResource = storageSyncManagementClient.SyncGroups.Create(resourceGroupName, storageSyncServiceResource.Name, syncGroupName, syncGroupParameters);
                Assert.NotNull(syncGroupResource);
                StorageSyncManagementTestUtilities.VerifySyncGroupProperties(syncGroupResource, true);

                CloudEndpoint cloudEndpointResource = storageSyncManagementClient.CloudEndpoints.Create(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, cloudEndpointName, cloudEndpointParameters);
                Assert.NotNull(cloudEndpointResource);
                StorageSyncManagementTestUtilities.VerifyCloudEndpointProperties(cloudEndpointResource, true);

                // invoke with directory path
                storageSyncManagementClient.CloudEndpoints.TriggerChangeDetection(
                    resourceGroupName: resourceGroupName,
                    storageSyncServiceName: storageSyncServiceName,
                    syncGroupName: syncGroupName,
                    cloudEndpointName: cloudEndpointName,
                    parameters: new TriggerChangeDetectionParameters(
                        directoryPath: "",
                        changeDetectionMode: ChangeDetectionMode.Recursive));

                // invoke with individual paths
                storageSyncManagementClient.CloudEndpoints.TriggerChangeDetection(
                    resourceGroupName: resourceGroupName,
                    storageSyncServiceName: storageSyncServiceName,
                    syncGroupName: syncGroupName,
                    cloudEndpointName: cloudEndpointName,
                    parameters: new TriggerChangeDetectionParameters(
                        paths: new string[] { "dir1/subdir1", "file.txt" }));

                storageSyncManagementClient.CloudEndpoints.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupName, cloudEndpointName);
                storageSyncManagementClient.SyncGroups.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupName);
                storageSyncManagementClient.StorageSyncServices.Delete(resourceGroupName, storageSyncServiceResource.Name);
                StorageSyncManagementTestUtilities.RemoveResourceGroup(resourcesClient, resourceGroupName);
            }
        }
        public async Task NonTransientErrorInFirstBatch_LetsTryTheRest_ButReportsSendFailure()
        {
            var batchSize = 10;

            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxy = ThrowingCloudProxy
                             .Create()
                             .WithBatchSize(batchSize)
                             .WithReportErrorInBatch(ThrowingCloudProxy.Throw <Exception>())
                             .WithReportSuccessfulBatch(2)
                             .Build();

            Task <Option <ICloudProxy> > GetCloudProxy(string id) => Task.FromResult(Option.Some(cloudProxy));

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            var sinkResult = await cloudMessageProcessor.ProcessAsync(GetMessages("device1", 3 * batchSize), CancellationToken.None);

            Assert.True(sinkResult.IsSuccessful); // non-transient errors are ignored, but reported in SendFailureDetails
            Assert.Equal(2 * batchSize, sinkResult.Succeeded.Count);
            Assert.Equal(0, sinkResult.Failed.Count);
            Assert.Equal(batchSize, sinkResult.InvalidDetailsList.Count);
            Assert.True(sinkResult.SendFailureDetails.HasValue);
            Assert.Equal(FailureKind.InvalidInput, sinkResult.SendFailureDetails.Expect(() => new Exception()).FailureKind);
        }
Beispiel #5
0
        public async Task TransientErrorInSecondBatch_OverwritesNonTransientFailureDetails()
        {
            var batchSize = 10;

            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxy = ThrowingCloudProxy
                             .CreateWithResponses(3, ThrowingCloudProxy.Success())
                             .Then(ThrowingCloudProxy.Throw <Exception>())                         // non-transient error
                             .ThenMany(batchSize - 4, ThrowingCloudProxy.Success())                // first batch ends here (3 success + 1 fail -> 10 - 4 to go)
                             .ThenMany(3, ThrowingCloudProxy.Success())
                             .Then(ThrowingCloudProxy.Throw <Client.Exceptions.IotHubException>()) // transient error
                             .ThenMany(batchSize - 4, ThrowingCloudProxy.Success())                // second batch ends here
                             .ThenMany(1 * batchSize, ThrowingCloudProxy.Success())
                             .Build();

            Task <Option <ICloudProxy> > GetCloudProxy(string id) => Task.FromResult(Option.Some(cloudProxy));

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            var sinkResult = await cloudMessageProcessor.ProcessAsync(GetMessages("device1", 3 * batchSize), CancellationToken.None);

            Assert.False(sinkResult.IsSuccessful);
            Assert.Equal(batchSize, sinkResult.Succeeded.Count);
            Assert.Equal(batchSize, sinkResult.Failed.Count);
            Assert.Equal(batchSize, sinkResult.InvalidDetailsList.Count);
            Assert.True(sinkResult.SendFailureDetails.HasValue);
            Assert.Equal(FailureKind.Transient, sinkResult.SendFailureDetails.Expect(() => new Exception()).FailureKind);
        }
        public async Task TransientErrorInSecondBatch_FastFailsRest_OverwritesNonTransientResult()
        {
            var batchSize = 10;

            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxy = ThrowingCloudProxy
                             .Create()
                             .WithBatchSize(batchSize)
                             .WithReportErrorInBatch(ThrowingCloudProxy.Throw <Exception>())
                             .WithReportErrorInBatch(ThrowingCloudProxy.Throw <Client.Exceptions.IotHubException>())
                             .WithReportSuccessfulBatch()
                             .Build();

            Task <Option <ICloudProxy> > GetCloudProxy(string id) => Task.FromResult(Option.Some(cloudProxy));

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            var sinkResult = await cloudMessageProcessor.ProcessAsync(GetMessages("device1", 3 * batchSize), CancellationToken.None);

            Assert.False(sinkResult.IsSuccessful);
            Assert.Equal(0, sinkResult.Succeeded.Count);
            Assert.Equal(2 * batchSize, sinkResult.Failed.Count);
            Assert.Equal(1 * batchSize, sinkResult.InvalidDetailsList.Count);
            Assert.True(sinkResult.SendFailureDetails.HasValue);
            Assert.Equal(FailureKind.Transient, sinkResult.SendFailureDetails.Expect(() => new Exception()).FailureKind);
        }
        public static void VerifyCloudEndpointProperties(CloudEndpoint resource, bool useDefaults)
        {
            Assert.NotNull(resource);
            Assert.NotNull(resource.Id);
            Assert.NotNull(resource.Name);

            if (useDefaults)
            {
            }
        }
Beispiel #8
0
        public void CloudMessageProcessor_CloseAsyncTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    cloudProxy      = Mock.Of <ICloudProxy>();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudEndpoint = new CloudEndpoint(cloudEndpointId, id => Task.FromResult(Option.Some(cloudProxy)), routingMessageConverter);

            IProcessor moduleMessageProcessor = cloudEndpoint.CreateProcessor();
            Task       result = moduleMessageProcessor.CloseAsync(CancellationToken.None);

            Assert.Equal(TaskEx.Done, result);
        }
Beispiel #9
0
        public void CloudEndpoint_CreateProcessorTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    cloudProxy      = Mock.Of <ICloudProxy>();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudEndpoint = new CloudEndpoint(cloudEndpointId, id => Task.FromResult(Option.Some(cloudProxy)), routingMessageConverter);

            IProcessor processor = cloudEndpoint.CreateProcessor();

            Assert.NotNull(processor);
            Assert.Equal(cloudEndpoint, processor.Endpoint);
        }
        public void BasicTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    cloudProxyMock  = new Mock <ICloudProxy>();
            string cloudEndpointId = Guid.NewGuid().ToString();

            Task <Option <ICloudProxy> > GetCloudProxy(string id) => Task.FromResult(Option.Some(cloudProxyMock.Object));

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            Assert.Equal(cloudEndpoint, cloudMessageProcessor.Endpoint);
            Assert.False(cloudMessageProcessor.ErrorDetectionStrategy.IsTransient(new Exception()));
        }
Beispiel #11
0
        public void CloudEndpointDeleteTest()
        {
            var handler = new RecordedDelegatingHandler {
                StatusCodeToReturn = HttpStatusCode.OK
            };

            using (MockContext context = MockContext.Start(this.GetType()))
            {
                IResourceManagementClient    resourcesClient             = StorageSyncManagementTestUtilities.GetResourceManagementClient(context, handler);
                IStorageSyncManagementClient storageSyncManagementClient = StorageSyncManagementTestUtilities.GetStorageSyncManagementClient(context, handler);

                // Create ResourceGroup
                string resourceGroupName = StorageSyncManagementTestUtilities.CreateResourceGroup(resourcesClient);

                // Create CloudEndpoint Name
                string storageSyncServiceName = TestUtilities.GenerateName("sss-cepdelete");
                string syncGroupName          = TestUtilities.GenerateName("sg-cepdelete");
                string cloudEndpointName      = TestUtilities.GenerateName("cepdelete");

                var storageSyncServiceParameters = StorageSyncManagementTestUtilities.GetDefaultStorageSyncServiceParameters();
                var syncGroupParameters          = StorageSyncManagementTestUtilities.GetDefaultSyncGroupParameters();
                var cloudEndpointParameters      = StorageSyncManagementTestUtilities.GetDefaultCloudEndpointParameters();

                StorageSyncService storageSyncServiceResource = storageSyncManagementClient.StorageSyncServices.Create(resourceGroupName, storageSyncServiceName, storageSyncServiceParameters);
                Assert.NotNull(storageSyncServiceResource);
                StorageSyncManagementTestUtilities.VerifyStorageSyncServiceProperties(storageSyncServiceResource, true);

                SyncGroup syncGroupResource = storageSyncManagementClient.SyncGroups.Create(resourceGroupName, storageSyncServiceResource.Name, syncGroupName, syncGroupParameters);
                Assert.NotNull(syncGroupResource);
                StorageSyncManagementTestUtilities.VerifySyncGroupProperties(syncGroupResource, true);

                // Delete CloudEndpoint before its created.
                storageSyncManagementClient.CloudEndpoints.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, cloudEndpointName);

                CloudEndpoint cloudEndpointResource = storageSyncManagementClient.CloudEndpoints.Create(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, cloudEndpointName, cloudEndpointParameters);
                Assert.NotNull(cloudEndpointResource);
                StorageSyncManagementTestUtilities.VerifyCloudEndpointProperties(cloudEndpointResource, true);

                // Delete CloudEndpoint
                storageSyncManagementClient.CloudEndpoints.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, cloudEndpointName);

                // Delete CloudEndpoint which was just deleted
                storageSyncManagementClient.CloudEndpoints.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, cloudEndpointName);

                storageSyncManagementClient.SyncGroups.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupName);
                storageSyncManagementClient.StorageSyncServices.Delete(resourceGroupName, storageSyncServiceResource.Name);
                StorageSyncManagementTestUtilities.RemoveResourceGroup(resourcesClient, resourceGroupName);
            }
        }
Beispiel #12
0
        public async Task Events_CloudProxyNotFoundWithDeviceInvalidStateException_DropMessage_Test()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    routingMessage  = Mock.Of <IRoutingMessage>();
            string cloudEndpointId = Guid.NewGuid().ToString();

            Mock.Get(routingMessage).Setup(rm => rm.SystemProperties).Returns(new Dictionary <string, string> {
                { "connectionDeviceId", "myConnectionDeviceId" }
            });
            var cloudEndpoint = new CloudEndpoint(cloudEndpointId, id => Task.FromResult(Try <ICloudProxy> .Failure(new DeviceInvalidStateException("Device removed from scope"))), routingMessageConverter, trackDeviceState: true);

            IProcessor cloudMessageProcessor         = cloudEndpoint.CreateProcessor();
            ISinkResult <IRoutingMessage> sinkResult = await cloudMessageProcessor.ProcessAsync(routingMessage, CancellationToken.None);

            Assert.Equal(FailureKind.InvalidInput, sinkResult.SendFailureDetails.OrDefault().FailureKind);
            Assert.Equal(typeof(DeviceInvalidStateException), sinkResult.SendFailureDetails.OrDefault().RawException.GetType());
        }
Beispiel #13
0
        public async Task Events_CloudProxyNotFoundTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    routingMessage  = Mock.Of <IRoutingMessage>();
            string cloudEndpointId = Guid.NewGuid().ToString();

            Mock.Get(routingMessage).Setup(rm => rm.SystemProperties).Returns(new Dictionary <string, string> {
                { "connectionDeviceId", "myConnectionDeviceId" }
            });
            var cloudEndpoint = new CloudEndpoint(cloudEndpointId, id => Task.FromResult(Option.None <ICloudProxy>()), routingMessageConverter);

            IProcessor cloudMessageProcessor         = cloudEndpoint.CreateProcessor();
            ISinkResult <IRoutingMessage> sinkResult = await cloudMessageProcessor.ProcessAsync(routingMessage, CancellationToken.None);

            Assert.Equal(FailureKind.None, sinkResult.SendFailureDetails.OrDefault().FailureKind);
            Assert.Equal(typeof(EdgeHubConnectionException), sinkResult.SendFailureDetails.OrDefault().RawException.GetType());
        }
Beispiel #14
0
        public async Task CloudMessageProcessor_ProcessAsyncTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    routingMessage  = Mock.Of <IRoutingMessage>();
            var    cloudProxy      = Mock.Of <ICloudProxy>(c => c.IsActive);
            string cloudEndpointId = Guid.NewGuid().ToString();

            Mock.Get(routingMessage).Setup(rm => rm.SystemProperties).Returns(new Dictionary <string, string> {
                { "connectionDeviceId", "myConnectionDeviceId" }
            });
            Mock.Get(cloudProxy).Setup(cp => cp.SendMessageAsync(It.IsAny <IMessage>())).Returns(Task.FromResult(true));
            var cloudEndpoint = new CloudEndpoint(cloudEndpointId, id => Task.FromResult(Option.Some(cloudProxy)), routingMessageConverter);

            IProcessor cloudMessageProcessor         = cloudEndpoint.CreateProcessor();
            ISinkResult <IRoutingMessage> sinkResult = await cloudMessageProcessor.ProcessAsync(routingMessage, CancellationToken.None);

            Assert.True(sinkResult.Succeeded.Contains(routingMessage));
        }
Beispiel #15
0
        public void CloudEndpoint_MembersTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    cloudProxy      = Mock.Of <ICloudProxy>();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudEndpoint = new CloudEndpoint(cloudEndpointId, (id) => Option.Some(cloudProxy), routingMessageConverter);

            Assert.Equal(cloudEndpointId, cloudEndpoint.Id);
            Assert.Equal("CloudEndpoint", cloudEndpoint.Type);
            Assert.Equal(cloudEndpointId, cloudEndpoint.Name);
            Assert.Equal(string.Empty, cloudEndpoint.IotHubName);

            IProcessor processor = cloudEndpoint.CreateProcessor();

            Assert.NotNull(processor);
            Assert.Equal(cloudEndpoint, processor.Endpoint);
        }
        public async Task TransientErrorOfFirstIdentity_DoesNotFastFailsSecondIdentity_ButReportsError()
        {
            var batchSize = 10;

            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            // this wont fast fail
            var cloudProxy1 = ThrowingCloudProxy
                              .Create()
                              .WithBatchSize(batchSize)
                              .WithReportSuccessfulBatch(3)
                              .Build();

            // this will fast fail after a batch (skipping the second and the third)
            var cloudProxy2 = ThrowingCloudProxy
                              .Create()
                              .WithBatchSize(batchSize)
                              .WithReportSuccessfulBatch()
                              .WithReportErrorInBatch(ThrowingCloudProxy.Throw <Client.Exceptions.IotHubException>())
                              .WithReportSuccessfulBatch()
                              .Build();

            var proxyMap = new Dictionary <string, ICloudProxy> {
                ["device1"] = cloudProxy1, ["device2"] = cloudProxy2
            };

            Task <Option <ICloudProxy> > GetCloudProxy(string id) => Task.FromResult(Option.Some(proxyMap[id]));

            var cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter);
            var cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            var random     = new Random(35325);
            var messages   = GetMessages("device1", 3 * batchSize).Concat(GetMessages("device2", 3 * batchSize)).OrderBy(order => random.Next()).ToList();
            var sinkResult = await cloudMessageProcessor.ProcessAsync(messages, CancellationToken.None);

            Assert.False(sinkResult.IsSuccessful);                                   // one batch went wrong, should report here
            Assert.Equal(3 * batchSize + 1 * batchSize, sinkResult.Succeeded.Count); // dev1 all good, dev2 1st good
            Assert.Equal(2 * batchSize, sinkResult.Failed.Count);
            Assert.Equal(0, sinkResult.InvalidDetailsList.Count);
            Assert.True(sinkResult.SendFailureDetails.HasValue);
            Assert.Equal(FailureKind.Transient, sinkResult.SendFailureDetails.Expect(() => new Exception()).FailureKind);
        }
Beispiel #17
0
        public async Task Events_DeviceIdNotFoundTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            var    routingMessage  = Mock.Of <IRoutingMessage>();
            string cloudEndpointId = Guid.NewGuid().ToString();

            Mock.Get(routingMessage).Setup(rm => rm.SystemProperties).Returns(new Dictionary <string, string> {
                { "messageId", "myConnectionDeviceId" }
            });
            var cloudProxy    = Mock.Of <ICloudProxy>();
            var cloudEndpoint = new CloudEndpoint(cloudEndpointId, id => Task.FromResult(Try.Success(cloudProxy)), routingMessageConverter, trackDeviceState: false);

            IProcessor cloudMessageProcessor         = cloudEndpoint.CreateProcessor();
            ISinkResult <IRoutingMessage> sinkResult = await cloudMessageProcessor.ProcessAsync(routingMessage, CancellationToken.None);

            Assert.Equal(FailureKind.InvalidInput, sinkResult.SendFailureDetails.OrDefault().FailureKind);
            Assert.Equal(typeof(InvalidOperationException), sinkResult.SendFailureDetails.OrDefault().RawException.GetType());
            Assert.Equal(1, sinkResult.InvalidDetailsList.Count);
            Assert.Equal(0, sinkResult.Failed.Count);
            Assert.Equal(0, sinkResult.Succeeded.Count);
        }
Beispiel #18
0
        public async Task RetryableExceptionsTest(Exception exception, bool isRetryable)
        {
            // Arrange
            string id         = "d1";
            var    cloudProxy = new Mock <ICloudProxy>();

            cloudProxy.Setup(c => c.SendMessageAsync(It.IsAny <IMessage>()))
            .ThrowsAsync(exception);
            var        cloudEndpoint = new CloudEndpoint(Guid.NewGuid().ToString(), _ => Task.FromResult(Option.Some(cloudProxy.Object)), new RoutingMessageConverter());
            IProcessor processor     = cloudEndpoint.CreateProcessor();
            var        message       = new RoutingMessage(TelemetryMessageSource.Instance, new byte[0], ImmutableDictionary <string, string> .Empty, new Dictionary <string, string>
            {
                [Core.SystemProperties.ConnectionDeviceId] = id
            });

            // Act
            ISinkResult <IRoutingMessage> result = await processor.ProcessAsync(message, CancellationToken.None);

            // Assert
            Assert.NotNull(result);
            if (isRetryable)
            {
                Assert.Equal(1, result.Failed.Count);
                Assert.Equal(0, result.Succeeded.Count);
                Assert.Equal(0, result.InvalidDetailsList.Count);
                Assert.Equal(message, result.Failed.First());
            }
            else
            {
                Assert.Equal(1, result.InvalidDetailsList.Count);
                Assert.Equal(0, result.Succeeded.Count);
                Assert.Equal(0, result.Failed.Count);
                Assert.Equal(message, result.InvalidDetailsList.First().Item);
                Assert.Equal(FailureKind.InvalidInput, result.InvalidDetailsList.First().FailureKind);
            }
        }
        public async Task ProcessAsyncTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxyMock = new Mock <ICloudProxy>();

            cloudProxyMock.Setup(c => c.SendMessageAsync(It.IsAny <IMessage>()))
            .Callback <IMessage>(
                msg =>
            {
                if (msg.Properties.ContainsKey("Delay"))
                {
                    Task.Delay(TimeSpan.FromSeconds(10)).Wait();
                }
            })
            .Returns(Task.CompletedTask);
            cloudProxyMock.SetupGet(p => p.IsActive).Returns(true);

            string device1Id = "device1";
            string device2Id = "device2";

            byte[] messageBody = Encoding.UTF8.GetBytes("Message body");
            var    properties  = new Dictionary <string, string>()
            {
                { "Prop1", "Val1" },
                { "Prop2", "Val2" }
            };

            var device1SystemProperties = new Dictionary <string, string>
            {
                { SystemProperties.DeviceId, device1Id }
            };

            var device2SystemProperties = new Dictionary <string, string>
            {
                { SystemProperties.DeviceId, device2Id }
            };

            var cancelProperties = new Dictionary <string, string>()
            {
                { "Delay", "true" },
                { "Prop2", "Val2" }
            };

            var message1 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, properties, device1SystemProperties);
            var message2 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, properties, device2SystemProperties);
            var message3 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, cancelProperties, device1SystemProperties);

            Task <Option <ICloudProxy> > GetCloudProxy(string id)
            {
                return(Task.FromResult(
                           id.Equals(device1Id)
                        ? Option.Some(cloudProxyMock.Object)
                        : Option.None <ICloudProxy>()));
            }

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter, maxBatchSize: 1);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            ISinkResult <IRoutingMessage> result1 = await cloudMessageProcessor.ProcessAsync(message1, CancellationToken.None);

            Assert.NotNull(result1);
            Assert.NotEmpty(result1.Succeeded);
            Assert.Empty(result1.Failed);
            Assert.Empty(result1.InvalidDetailsList);
            Assert.False(result1.SendFailureDetails.HasValue);

            ISinkResult <IRoutingMessage> result2 = await cloudMessageProcessor.ProcessAsync(message2, CancellationToken.None);

            Assert.NotNull(result2);
            Assert.Empty(result2.InvalidDetailsList);
            Assert.NotEmpty(result2.Failed);
            Assert.Empty(result2.Succeeded);
            Assert.True(result2.SendFailureDetails.HasValue);

            ISinkResult <IRoutingMessage> resultBatch = await cloudMessageProcessor.ProcessAsync(new[] { message1, message2 }, CancellationToken.None);

            Assert.NotNull(resultBatch);
            Assert.Equal(1, resultBatch.Succeeded.Count);
            Assert.Equal(1, resultBatch.Failed.Count);
            Assert.Empty(resultBatch.InvalidDetailsList);
            Assert.True(resultBatch.SendFailureDetails.HasValue);

            ISinkResult <IRoutingMessage> resultBatchCancelled = await cloudMessageProcessor.ProcessAsync(new[] { message1, message2 }, new CancellationToken(true));

            Assert.NotNull(resultBatchCancelled);
            Assert.Empty(resultBatchCancelled.Succeeded);
            Assert.NotEmpty(resultBatchCancelled.Failed);
            Assert.Empty(resultBatchCancelled.InvalidDetailsList);
            Assert.True(resultBatchCancelled.SendFailureDetails.HasValue);

            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
            ISinkResult <IRoutingMessage> resultBatchCancelled2 = await cloudMessageProcessor.ProcessAsync(new[] { message1, message3, message1 }, cts.Token);

            Assert.NotNull(resultBatchCancelled2);
            Assert.Equal(2, resultBatchCancelled2.Succeeded.Count);
            Assert.Equal(1, resultBatchCancelled2.Failed.Count);
            Assert.Empty(resultBatchCancelled2.InvalidDetailsList);
            Assert.True(resultBatchCancelled2.SendFailureDetails.HasValue);
        }
        public async Task ProcessInBatchesWithBatchSizeTest()
        {
            // Arrange
            string device1 = "d1";
            string device2 = "d2";
            string device3 = "d3";

            IList <IRoutingMessage> device1Messages = GetMessages(device1, 45);
            IList <IRoutingMessage> device2Messages = GetMessages(device2, 25);
            IList <IRoutingMessage> device3Messages = GetMessages(device3, 30);

            IList <IRoutingMessage> messagesToProcess = device1Messages
                                                        .Concat(device2Messages)
                                                        .Concat(device3Messages)
                                                        .ToList();

            Mock <ICloudProxy> InitCloudProxy(List <int> receivedMsgCountList)
            {
                var cp = new Mock <ICloudProxy>();

                cp.Setup(c => c.SendMessageBatchAsync(It.IsAny <IEnumerable <IMessage> >()))
                .Callback <IEnumerable <IMessage> >(b => receivedMsgCountList.Add(b.Count()))
                .Returns(Task.CompletedTask);
                cp.SetupGet(p => p.IsActive).Returns(true);
                return(cp);
            }

            var device1CloudReceivedMessagesCountList = new List <int>();
            Mock <ICloudProxy> device1CloudProxy      = InitCloudProxy(device1CloudReceivedMessagesCountList);

            var device2CloudReceivedMessagesCountList = new List <int>();
            Mock <ICloudProxy> device2CloudProxy      = InitCloudProxy(device2CloudReceivedMessagesCountList);

            var device3CloudReceivedMessagesCountList = new List <int>();
            Mock <ICloudProxy> device3CloudProxy      = InitCloudProxy(device3CloudReceivedMessagesCountList);

            Task <Option <ICloudProxy> > GetCloudProxy(string id)
            {
                ICloudProxy cp = null;

                if (id == device1)
                {
                    cp = device1CloudProxy.Object;
                }
                else if (id == device2)
                {
                    cp = device2CloudProxy.Object;
                }
                else if (id == device3)
                {
                    cp = device3CloudProxy.Object;
                }

                return(Task.FromResult(Option.Maybe(cp)));
            }

            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();
            var    cloudEndpoint   = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter, 30);

            // Act
            IProcessor cloudMessageProcessor         = cloudEndpoint.CreateProcessor();
            ISinkResult <IRoutingMessage> sinkResult = await cloudMessageProcessor.ProcessAsync(messagesToProcess, CancellationToken.None);

            // Assert
            Assert.Equal(messagesToProcess, sinkResult.Succeeded);
            Assert.Equal(device1CloudReceivedMessagesCountList, new[] { 30, 15 });
            Assert.Equal(device2CloudReceivedMessagesCountList, new[] { 25 });
            Assert.Equal(device3CloudReceivedMessagesCountList, new[] { 30 });
        }
        public async Task ProcessAsync_SendThrows_Test()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxyMock = new Mock <ICloudProxy>();

            cloudProxyMock.Setup(c => c.SendMessageAsync(It.IsAny <IMessage>()))
            .Throws <TimeoutException>();
            cloudProxyMock.SetupGet(p => p.IsActive).Returns(true);

            string device1Id = "device1";
            string device2Id = "device2";

            byte[] messageBody = Encoding.UTF8.GetBytes("Message body");
            var    properties  = new Dictionary <string, string>()
            {
                { "Prop1", "Val1" },
                { "Prop2", "Val2" }
            };

            var device1SystemProperties = new Dictionary <string, string>
            {
                { SystemProperties.DeviceId, device1Id }
            };

            var device2SystemProperties = new Dictionary <string, string>
            {
                { SystemProperties.DeviceId, device2Id }
            };

            var message1 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, properties, device1SystemProperties);
            var message2 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, properties, device2SystemProperties);

            Task <Option <ICloudProxy> > GetCloudProxy(string id)
            {
                return(Task.FromResult(Option.Some(cloudProxyMock.Object)));
            }

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            ISinkResult <IRoutingMessage> result = await cloudMessageProcessor.ProcessAsync(new[] { message1, message2 }, CancellationToken.None);

            Assert.NotNull(result);
            Assert.Empty(result.Succeeded);
            Assert.Equal(2, result.Failed.Count);
            Assert.Empty(result.InvalidDetailsList);
            Assert.True(result.SendFailureDetails.HasValue);

            // throw non-retryable
            cloudProxyMock.Setup(c => c.SendMessageAsync(It.IsAny <IMessage>()))
            .Throws <Exception>();

            ISinkResult <IRoutingMessage> result1 = await cloudMessageProcessor.ProcessAsync(new[] { message1, message2 }, CancellationToken.None);

            Assert.NotNull(result1);
            Assert.Empty(result1.Succeeded);
            Assert.Empty(result1.Failed);
            Assert.Equal(2, result1.InvalidDetailsList.Count);
            Assert.True(result1.SendFailureDetails.HasValue);
        }
        public void ServerEndpointAllOperationsTest()
        {
            var handler = new RecordedDelegatingHandler {
                StatusCodeToReturn = HttpStatusCode.OK
            };

            using (MockContext context = MockContext.Start(this.GetType()))
            {
                IResourceManagementClient    resourcesClient             = StorageSyncManagementTestUtilities.GetResourceManagementClient(context, handler);
                IStorageSyncManagementClient storageSyncManagementClient = StorageSyncManagementTestUtilities.GetStorageSyncManagementClient(context, handler);

                // Create ResourceGroup
                string resourceGroupName = StorageSyncManagementTestUtilities.CreateResourceGroup(resourcesClient);

                // Create ServerEndpoint
                string storageSyncServiceName = TestUtilities.GenerateName("sss-sepall");
                string syncGroupName          = TestUtilities.GenerateName("sg-sepall");
                string resourceName           = TestUtilities.GenerateName("sepall");

                var storageSyncServiceParameters = StorageSyncManagementTestUtilities.GetDefaultStorageSyncServiceParameters();
                var syncGroupParameters          = StorageSyncManagementTestUtilities.GetDefaultSyncGroupParameters();
                var cloudEndpointParameters      = StorageSyncManagementTestUtilities.GetDefaultCloudEndpointParameters();

                StorageSyncService storageSyncServiceResource = storageSyncManagementClient.StorageSyncServices.Create(resourceGroupName, storageSyncServiceName, storageSyncServiceParameters);
                Assert.NotNull(storageSyncServiceResource);
                StorageSyncManagementTestUtilities.VerifyStorageSyncServiceProperties(storageSyncServiceResource, true);

                SyncGroup syncGroupResource = storageSyncManagementClient.SyncGroups.Create(resourceGroupName, storageSyncServiceResource.Name, syncGroupName, syncGroupParameters);
                Assert.NotNull(syncGroupResource);
                StorageSyncManagementTestUtilities.VerifySyncGroupProperties(syncGroupResource, true);

                CloudEndpoint cloudEndpointResource = storageSyncManagementClient.CloudEndpoints.Create(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, resourceName, cloudEndpointParameters);
                Assert.NotNull(cloudEndpointResource);
                StorageSyncManagementTestUtilities.VerifyCloudEndpointProperties(cloudEndpointResource, true);

                RegisteredServer registeredServerResource = EnsureRegisteredServerResource(storageSyncManagementClient, resourceGroupName, storageSyncServiceName, syncGroupName, storageSyncServiceResource);
                Assert.NotNull(registeredServerResource);

                StorageSyncManagementTestUtilities.VerifyRegisteredServerProperties(registeredServerResource, true);

                var serverEndpointParameters       = StorageSyncManagementTestUtilities.GetDefaultServerEndpointParameters(registeredServerResource.Id);
                var serverEndpointUpdateParameters = StorageSyncManagementTestUtilities.GetDefaultServerEndpointUpdateParameters();

                // Delete Test before it exists.
                storageSyncManagementClient.ServerEndpoints.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, resourceName);

                ServerEndpoint serverEndpointResource = storageSyncManagementClient.ServerEndpoints.Create(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, resourceName, serverEndpointParameters);
                Assert.NotNull(serverEndpointResource);
                StorageSyncManagementTestUtilities.VerifyServerEndpointProperties(serverEndpointResource, true);

                // GET Test
                serverEndpointResource = storageSyncManagementClient.ServerEndpoints.Get(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, resourceName);
                Assert.NotNull(serverEndpointResource);
                StorageSyncManagementTestUtilities.VerifyServerEndpointProperties(serverEndpointResource, true);

                // List Test
                IEnumerable <ServerEndpoint> serverEndpoints = storageSyncManagementClient.ServerEndpoints.ListBySyncGroup(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name);
                Assert.Single(serverEndpoints);
                Assert.NotNull(serverEndpoints.Single());
                StorageSyncManagementTestUtilities.VerifyServerEndpointProperties(serverEndpoints.Single(), true);

                // Recall Test
                RecallActionParameters             recallActionParameters             = StorageSyncManagementTestUtilities.GetDefaultRecallActionParameters();
                ServerEndpointsRecallActionHeaders serverEndpointsRecallActionHeaders = storageSyncManagementClient.ServerEndpoints.RecallAction(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, resourceName, recallActionParameters);
                Assert.NotNull(serverEndpointsRecallActionHeaders);
                Assert.NotEmpty(serverEndpointsRecallActionHeaders.XMsCorrelationRequestId);
                Assert.NotEmpty(serverEndpointsRecallActionHeaders.XMsRequestId);

                // Update Test
                serverEndpointResource = storageSyncManagementClient.ServerEndpoints.Update(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, resourceName, serverEndpointUpdateParameters);
                Assert.NotNull(serverEndpointResource);
                StorageSyncManagementTestUtilities.VerifyServerEndpointUpdateProperties(serverEndpointResource, true);

                // Delete Test
                storageSyncManagementClient.ServerEndpoints.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupResource.Name, resourceName);

                storageSyncManagementClient.CloudEndpoints.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupName, resourceName);
                storageSyncManagementClient.SyncGroups.Delete(resourceGroupName, storageSyncServiceResource.Name, syncGroupName);
                storageSyncManagementClient.RegisteredServers.Delete(resourceGroupName, storageSyncServiceResource.Name, registeredServerResource.ServerId.Trim('"'));
                storageSyncManagementClient.StorageSyncServices.Delete(resourceGroupName, storageSyncServiceResource.Name);
                StorageSyncManagementTestUtilities.RemoveResourceGroup(resourcesClient, resourceGroupName);
            }
        }
        public async Task ProcessAsyncTest()
        {
            Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter();
            string cloudEndpointId = Guid.NewGuid().ToString();

            var cloudProxyMock = new Mock <ICloudProxy>();

            cloudProxyMock.Setup(c => c.SendMessageAsync(It.IsAny <IMessage>()))
            .Callback <IMessage>(
                msg =>
            {
                if (msg.Properties.ContainsKey("Delay"))
                {
                    Task.Delay(TimeSpan.FromSeconds(10)).Wait();
                }
            })
            .Returns(Task.CompletedTask);
            cloudProxyMock.SetupGet(p => p.IsActive).Returns(true);

            string device1Id = "device1";
            string device2Id = "device2";
            string device3Id = "device3";

            byte[] messageBody = Encoding.UTF8.GetBytes("Message body");
            var    properties  = new Dictionary <string, string>()
            {
                { "Prop1", "Val1" },
                { "Prop2", "Val2" }
            };

            var device1SystemProperties = new Dictionary <string, string>
            {
                { SystemProperties.DeviceId, device1Id }
            };

            var device2SystemProperties = new Dictionary <string, string>
            {
                { SystemProperties.DeviceId, device2Id }
            };

            var device3SystemProperties = new Dictionary <string, string>
            {
                { SystemProperties.DeviceId, device3Id }
            };

            var cancelProperties = new Dictionary <string, string>()
            {
                { "Delay", "true" },
                { "Prop2", "Val2" }
            };

            var message1 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, properties, device1SystemProperties);
            var message2 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, properties, device2SystemProperties);
            var message3 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, cancelProperties, device1SystemProperties);
            var message4 = new RoutingMessage(TelemetryMessageSource.Instance, messageBody, properties, device3SystemProperties);

            Task <Try <ICloudProxy> > GetCloudProxy(string id)
            {
                if (id == device1Id)
                {
                    return(Task.FromResult(Try.Success(cloudProxyMock.Object)));
                }
                else if (id == device2Id)
                {
                    return(Task.FromResult(Try <ICloudProxy> .Failure(new DeviceInvalidStateException())));
                }
                else
                {
                    return(Task.FromResult(Try <ICloudProxy> .Failure(new Exception())));
                }
            }

            var        cloudEndpoint         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter, false, maxBatchSize: 1);
            IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor();

            ISinkResult <IRoutingMessage> result1 = await cloudMessageProcessor.ProcessAsync(message1, CancellationToken.None);

            Assert.NotNull(result1);
            Assert.NotEmpty(result1.Succeeded);
            Assert.Empty(result1.Failed);
            Assert.Empty(result1.InvalidDetailsList);
            Assert.False(result1.SendFailureDetails.HasValue);

            ISinkResult <IRoutingMessage> result2 = await cloudMessageProcessor.ProcessAsync(message2, CancellationToken.None);

            Assert.NotNull(result2);
            Assert.Empty(result2.InvalidDetailsList);
            Assert.NotEmpty(result2.Failed);
            Assert.Empty(result2.Succeeded);
            Assert.True(result2.SendFailureDetails.HasValue);

            ISinkResult <IRoutingMessage> resultBatch = await cloudMessageProcessor.ProcessAsync(new[] { message1, message2 }, CancellationToken.None);

            Assert.NotNull(resultBatch);
            Assert.Equal(1, resultBatch.Succeeded.Count);
            Assert.Equal(1, resultBatch.Failed.Count);
            Assert.Empty(resultBatch.InvalidDetailsList);
            Assert.True(resultBatch.SendFailureDetails.HasValue);

            ISinkResult <IRoutingMessage> resultBatchCancelled = await cloudMessageProcessor.ProcessAsync(new[] { message1, message2 }, new CancellationToken(true));

            Assert.NotNull(resultBatchCancelled);
            Assert.Empty(resultBatchCancelled.Succeeded);
            Assert.NotEmpty(resultBatchCancelled.Failed);
            Assert.Empty(resultBatchCancelled.InvalidDetailsList);
            Assert.True(resultBatchCancelled.SendFailureDetails.HasValue);

            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
            ISinkResult <IRoutingMessage> resultBatchCancelled2 = await cloudMessageProcessor.ProcessAsync(new[] { message1, message3, message1 }, cts.Token);

            Assert.NotNull(resultBatchCancelled2);
            Assert.Equal(2, resultBatchCancelled2.Succeeded.Count);
            Assert.Equal(1, resultBatchCancelled2.Failed.Count);
            Assert.Empty(resultBatchCancelled2.InvalidDetailsList);
            Assert.True(resultBatchCancelled2.SendFailureDetails.HasValue);

            // Should handle as no connection on unhandled exception
            ISinkResult <IRoutingMessage> result4 = await cloudMessageProcessor.ProcessAsync(message4, CancellationToken.None);

            Assert.NotNull(result4);
            Assert.Empty(result4.InvalidDetailsList);
            Assert.NotEmpty(result4.Failed);
            Assert.Empty(result4.Succeeded);
            Assert.True(result4.SendFailureDetails.HasValue);

            // Initialize CloudEndpoint with trackDeviceState=true
            var cloudEndpoint2         = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter, true, maxBatchSize: 1);
            var cloudMessageProcessor2 = cloudEndpoint2.CreateProcessor();

            // Should fail and mark operation invalid on UnauthorizedException which will drop the message
            var invalidOnDeviceInvalidStateException = await cloudMessageProcessor2.ProcessAsync(message2, CancellationToken.None);

            Assert.NotNull(invalidOnDeviceInvalidStateException);
            Assert.NotEmpty(invalidOnDeviceInvalidStateException.InvalidDetailsList);
            Assert.Empty(invalidOnDeviceInvalidStateException.Failed);
            Assert.Empty(invalidOnDeviceInvalidStateException.Succeeded);
            Assert.True(invalidOnDeviceInvalidStateException.SendFailureDetails.HasValue);

            // Should fail and mark operation invalid on unhandled exception which will drop the message
            var invalidOnUnhandledException = await cloudMessageProcessor2.ProcessAsync(message4, CancellationToken.None);

            Assert.NotNull(invalidOnUnhandledException);
            Assert.NotEmpty(invalidOnUnhandledException.InvalidDetailsList);
            Assert.Empty(invalidOnUnhandledException.Failed);
            Assert.Empty(invalidOnUnhandledException.Succeeded);
            Assert.True(invalidOnUnhandledException.SendFailureDetails.HasValue);
        }