Beispiel #1
0
        public async Task HandleDeviceConnectedDisconnectedEvents()
        {
            // Arrange
            var edgeHubIdentity = Mock.Of <IModuleIdentity>(i => i.Id == "edgeD1/$edgeHub");
            var twinManager     = Mock.Of <ITwinManager>();
            var routeFactory    = new EdgeRouteFactory(Mock.Of <IEndpointFactory>());
            var twinCollectionMessageConverter = new TwinCollectionMessageConverter();
            var twinMessageConverter           = new TwinMessageConverter();
            var versionInfo = new VersionInfo("1.0", "1", "123");
            var deviceScopeIdentitiesCache = Mock.Of <IDeviceScopeIdentitiesCache>();
            var edgeHubConnection          = new EdgeHubConnection(edgeHubIdentity, twinManager, routeFactory, twinCollectionMessageConverter, twinMessageConverter, versionInfo, deviceScopeIdentitiesCache);

            Mock.Get(twinManager).Setup(t => t.UpdateReportedPropertiesAsync(It.IsAny <string>(), It.IsAny <IMessage>())).Returns(Task.CompletedTask);

            // Act
            edgeHubConnection.DeviceConnected(this, Mock.Of <IIdentity>(i => i.Id == "d1"));
            edgeHubConnection.DeviceConnected(this, Mock.Of <IIdentity>(i => i.Id == "edgeD1/$edgeHub"));

            // Assert
            await Task.Delay(TimeSpan.FromSeconds(2));

            Mock.Get(twinManager).Verify(t => t.UpdateReportedPropertiesAsync(It.IsAny <string>(), It.IsAny <IMessage>()), Times.Once);

            // Act
            edgeHubConnection.DeviceDisconnected(this, Mock.Of <IIdentity>(i => i.Id == "d1"));
            edgeHubConnection.DeviceDisconnected(this, Mock.Of <IIdentity>(i => i.Id == "edgeD1/$edgeHub"));

            // Assert
            await Task.Delay(TimeSpan.FromSeconds(2));

            Mock.Get(twinManager).Verify(t => t.UpdateReportedPropertiesAsync(It.IsAny <string>(), It.IsAny <IMessage>()), Times.Exactly(2));
        }
Beispiel #2
0
        public static TwinConfigSource GetTwinConfigSource(Option <X509Certificate2> manifestTrustBundle)
        {
            var            connectionManager = new ConnectionManager(Mock.Of <ICloudConnectionProvider>(), Mock.Of <ICredentialsCache>(), Mock.Of <IIdentityProvider>(), Mock.Of <IDeviceConnectivityManager>());
            var            endpointFactory   = new EndpointFactory(connectionManager, new RoutingMessageConverter(), "testHubEdgeDevice1", 10, 10, false);
            var            routeFactory      = new EdgeRouteFactory(endpointFactory);
            var            configParser      = new EdgeHubConfigParser(routeFactory, new BrokerPropertiesValidator());
            var            twinCollectionMessageConverter = new TwinCollectionMessageConverter();
            var            twinMessageConverter           = new TwinMessageConverter();
            var            dbStoreProvider            = new InMemoryDbStoreProvider();
            IStoreProvider storeProvider              = new StoreProvider(dbStoreProvider);
            IEntityStore <string, TwinInfo> twinStore = storeProvider.GetEntityStore <string, TwinInfo>("twins");
            var twinManager = new TwinManager(connectionManager, twinCollectionMessageConverter, twinMessageConverter, Option.Some(twinStore));
            var versionInfo = new VersionInfo(string.Empty, string.Empty, string.Empty);

            // Create Edge Hub connection
            EdgeHubConnection edgeHubConnection = GetEdgeHubConnection().Result;

            // TwinConfig Source
            return(new TwinConfigSource(
                       edgeHubConnection,
                       string.Empty,
                       versionInfo,
                       twinManager,
                       twinMessageConverter,
                       twinCollectionMessageConverter,
                       configParser,
                       manifestTrustBundle));
        }
Beispiel #3
0
        public void ConvertedMessageHasAnEnqueuedTimeProperty()
        {
            IMessage actualMessage = new TwinCollectionMessageConverter().ToMessage(new TwinCollection());

            Assert.InRange(
                DateTime.Parse(actualMessage.SystemProperties[SystemProperties.EnqueuedTime], null, DateTimeStyles.RoundtripKind),
                DateTime.UtcNow.Subtract(new TimeSpan(0, 1, 0)), DateTime.UtcNow);
        }
        public async Task UpdateReportedPropertiesTest()
        {
            string id = "d1";

            var reported1 = new TwinCollection
            {
                ["p1"] = "vp1",
                ["p3"] = "v3"
            };

            TwinCollection receivedTwinPatch = null;
            var            twinStore         = new Mock <ITwinStore>(MockBehavior.Strict);

            twinStore.Setup(c => c.UpdateReportedProperties(id, It.IsAny <TwinCollection>()))
            .Callback <string, TwinCollection>((s, t) => receivedTwinPatch = t)
            .Returns(Task.CompletedTask);

            TwinCollection receivedTwinPatch2      = null;
            var            reportedPropertiesStore = new Mock <IReportedPropertiesStore>(MockBehavior.Strict);

            reportedPropertiesStore.Setup(r => r.InitSyncToCloud(id));
            reportedPropertiesStore.Setup(r => r.Update(id, It.IsAny <TwinCollection>()))
            .Callback <string, TwinCollection>((s, t) => receivedTwinPatch2 = t)
            .Returns(Task.CompletedTask);

            var cloudSync                   = Mock.Of <ICloudSync>();
            var twinMessageConverter        = new TwinMessageConverter();
            var connectionManager           = Mock.Of <IConnectionManager>();
            var twinCollectionConverter     = new TwinCollectionMessageConverter();
            var reportedPropertiesValidator = Mock.Of <IValidator <TwinCollection> >();
            var deviceConnectivityManager   = Mock.Of <IDeviceConnectivityManager>();

            var twinManager = new StoringTwinManager(
                connectionManager,
                twinCollectionConverter,
                twinMessageConverter,
                reportedPropertiesValidator,
                twinStore.Object,
                reportedPropertiesStore.Object,
                cloudSync,
                deviceConnectivityManager,
                TimeSpan.FromMinutes(10));

            IMessage reportedPropertiesMessage = twinCollectionConverter.ToMessage(reported1);

            // Act
            await twinManager.UpdateReportedPropertiesAsync(id, reportedPropertiesMessage);

            // Assert
            twinStore.VerifyAll();
            reportedPropertiesStore.VerifyAll();

            Assert.NotNull(receivedTwinPatch);
            Assert.NotNull(receivedTwinPatch2);
            Assert.Equal(reported1.ToJson(), receivedTwinPatch.ToJson());
            Assert.Equal(reported1.ToJson(), receivedTwinPatch2.ToJson());
        }
        public async Task DeviceConnectionNoSubscriptionTest()
        {
            string id       = "d1";
            var    identity = Mock.Of <IIdentity>(i => i.Id == id);

            var twinStore = new Mock <ITwinStore>(MockBehavior.Strict);

            twinStore.Setup(c => c.Get(id))
            .ReturnsAsync(Option.None <Twin>());

            var reportedPropertiesStore = new Mock <IReportedPropertiesStore>(MockBehavior.Strict);

            reportedPropertiesStore.Setup(r => r.SyncToCloud(id))
            .Returns(Task.CompletedTask);

            var deviceProxy                 = new Mock <IDeviceProxy>(MockBehavior.Strict);
            var cloudSync                   = new Mock <ICloudSync>(MockBehavior.Strict);
            var twinMessageConverter        = new TwinMessageConverter();
            var twinCollectionConverter     = new TwinCollectionMessageConverter();
            var reportedPropertiesValidator = Mock.Of <IValidator <TwinCollection> >();

            var connectionManager = Mock.Of <IConnectionManager>(
                c =>
                c.CheckClientSubscription(id, DeviceSubscription.DesiredPropertyUpdates) == false &&
                c.GetConnectedClients() == new[] { identity });

            var deviceConnectivityManager = new Mock <IDeviceConnectivityManager>();

            var twinManager = new StoringTwinManager(
                connectionManager,
                twinCollectionConverter,
                twinMessageConverter,
                reportedPropertiesValidator,
                twinStore.Object,
                reportedPropertiesStore.Object,
                cloudSync.Object,
                deviceConnectivityManager.Object,
                TimeSpan.FromMinutes(10));

            // Act
            deviceConnectivityManager.Raise(d => d.DeviceConnected += null, this, new EventArgs());

            // Assert
            await Task.Delay(TimeSpan.FromSeconds(3));

            twinStore.VerifyAll();
            reportedPropertiesStore.VerifyAll();
            Mock.Get(connectionManager).VerifyAll();
            cloudSync.VerifyAll();
            deviceProxy.VerifyAll();
        }
Beispiel #6
0
        public void ConvertsTwinCollectionsToMqttMessages(TwinCollection collection, string expectedJson)
        {
            EdgeMessage expectedMessage = new EdgeMessage.Builder(expectedJson.ToBody())
                                          .SetSystemProperties(new Dictionary <string, string>()
            {
                [SystemProperties.EnqueuedTime] = "",
                [SystemProperties.Version]      = collection.Version.ToString()
            })
                                          .Build();
            IMessage actualMessage = new TwinCollectionMessageConverter().ToMessage(collection);

            Assert.Equal(expectedMessage.Body, actualMessage.Body);
            Assert.Equal(expectedMessage.Properties, actualMessage.Properties);
            Assert.Equal(expectedMessage.SystemProperties.Keys, actualMessage.SystemProperties.Keys);
            Assert.Equal(expectedMessage.SystemProperties[SystemProperties.Version], actualMessage.SystemProperties[SystemProperties.Version]);
        }
Beispiel #7
0
        public static async Task <EdgeHubConnection> GetEdgeHubConnection()
        {
            var            connectionManager = new ConnectionManager(Mock.Of <ICloudConnectionProvider>(), Mock.Of <ICredentialsCache>(), Mock.Of <IIdentityProvider>(), Mock.Of <IDeviceConnectivityManager>());
            var            endpointFactory   = new EndpointFactory(connectionManager, new RoutingMessageConverter(), "testHubEdgeDevice1", 10, 10, false);
            var            routeFactory      = new EdgeRouteFactory(endpointFactory);
            var            twinCollectionMessageConverter = new TwinCollectionMessageConverter();
            var            twinMessageConverter           = new TwinMessageConverter();
            var            dbStoreProvider            = new InMemoryDbStoreProvider();
            IStoreProvider storeProvider              = new StoreProvider(dbStoreProvider);
            IEntityStore <string, TwinInfo> twinStore = storeProvider.GetEntityStore <string, TwinInfo>("twins");
            var twinManager = new TwinManager(connectionManager, twinCollectionMessageConverter, twinMessageConverter, Option.Some(twinStore));
            var versionInfo = new VersionInfo(string.Empty, string.Empty, string.Empty);

            return(await EdgeHubConnection.Create(
                       Mock.Of <IIdentity>(i => i.Id == "someid"),
                       Mock.Of <IEdgeHub>(),
                       twinManager,
                       Mock.Of <IConnectionManager>(),
                       routeFactory,
                       twinCollectionMessageConverter,
                       versionInfo,
                       new NullDeviceScopeIdentitiesCache()));
        }
        public async Task TestEdgeHubConnection()
        {
            const string EdgeDeviceId                   = "testHubEdgeDevice1";
            var          twinMessageConverter           = new TwinMessageConverter();
            var          twinCollectionMessageConverter = new TwinCollectionMessageConverter();
            var          messageConverterProvider       = new MessageConverterProvider(
                new Dictionary <Type, IMessageConverter>()
            {
                { typeof(Message), new DeviceClientMessageConverter() },
                { typeof(Twin), twinMessageConverter },
                { typeof(TwinCollection), twinCollectionMessageConverter }
            });

            string iotHubConnectionString = await SecretsHelper.GetSecretFromConfigKey("iotHubConnStrKey");

            IotHubConnectionStringBuilder iotHubConnectionStringBuilder = IotHubConnectionStringBuilder.Create(iotHubConnectionString);
            RegistryManager registryManager = RegistryManager.CreateFromConnectionString(iotHubConnectionString);
            await registryManager.OpenAsync();

            string iothubHostName   = iotHubConnectionStringBuilder.HostName;
            var    identityProvider = new IdentityProvider(iothubHostName);
            var    identityFactory  = new ClientCredentialsFactory(identityProvider);

            (string edgeDeviceId, string deviceConnStr) = await RegistryManagerHelper.CreateDevice(EdgeDeviceId, iotHubConnectionString, registryManager, true, false);

            string edgeHubConnectionString = $"{deviceConnStr};ModuleId={EdgeHubModuleId}";

            IClientCredentials edgeHubCredentials = identityFactory.GetWithConnectionString(edgeHubConnectionString);
            string             sasKey             = ConnectionStringHelper.GetSharedAccessKey(deviceConnStr);
            var signatureProvider = new SharedAccessKeySignatureProvider(sasKey);
            var credentialsCache  = Mock.Of <ICredentialsCache>();
            var metadataStore     = new Mock <IMetadataStore>();

            metadataStore.Setup(m => m.GetMetadata(It.IsAny <string>())).ReturnsAsync(new ConnectionMetadata("dummyValue"));
            var cloudConnectionProvider = new CloudConnectionProvider(
                messageConverterProvider,
                1,
                new ClientProvider(),
                Option.None <UpstreamProtocol>(),
                new ClientTokenProvider(signatureProvider, iothubHostName, edgeDeviceId, TimeSpan.FromMinutes(60)),
                Mock.Of <IDeviceScopeIdentitiesCache>(),
                credentialsCache,
                edgeHubCredentials.Identity,
                TimeSpan.FromMinutes(60),
                true,
                TimeSpan.FromSeconds(20),
                false,
                Option.None <IWebProxy>(),
                metadataStore.Object);
            var deviceConnectivityManager = Mock.Of <IDeviceConnectivityManager>();
            var connectionManager         = new ConnectionManager(cloudConnectionProvider, Mock.Of <ICredentialsCache>(), identityProvider, deviceConnectivityManager);

            try
            {
                Mock.Get(credentialsCache)
                .Setup(c => c.Get(edgeHubCredentials.Identity))
                .ReturnsAsync(Option.Some(edgeHubCredentials));
                Assert.NotNull(edgeHubCredentials);
                Assert.NotNull(edgeHubCredentials.Identity);

                // Set Edge hub desired properties
                await this.SetDesiredProperties(registryManager, edgeDeviceId);

                var endpointFactory = new EndpointFactory(connectionManager, new RoutingMessageConverter(), edgeDeviceId, 10, 10);
                var routeFactory    = new EdgeRouteFactory(endpointFactory);

                var            dbStoreProvider            = new InMemoryDbStoreProvider();
                IStoreProvider storeProvider              = new StoreProvider(dbStoreProvider);
                IEntityStore <string, TwinInfo> twinStore = storeProvider.GetEntityStore <string, TwinInfo>("twins");
                var      twinManager             = new TwinManager(connectionManager, twinCollectionMessageConverter, twinMessageConverter, Option.Some(twinStore));
                var      routerConfig            = new RouterConfig(Enumerable.Empty <Route>());
                TimeSpan defaultTimeout          = TimeSpan.FromSeconds(60);
                var      endpointExecutorFactory = new SyncEndpointExecutorFactory(new EndpointExecutorConfig(defaultTimeout, new FixedInterval(0, TimeSpan.FromSeconds(1)), defaultTimeout, true));
                Router   router = await Router.CreateAsync(Guid.NewGuid().ToString(), iothubHostName, routerConfig, endpointExecutorFactory);

                IInvokeMethodHandler invokeMethodHandler = new InvokeMethodHandler(connectionManager);
                var      subscriptionProcessor           = new SubscriptionProcessor(connectionManager, invokeMethodHandler, deviceConnectivityManager);
                IEdgeHub edgeHub = new RoutingEdgeHub(router, new RoutingMessageConverter(), connectionManager, twinManager, edgeDeviceId, invokeMethodHandler, subscriptionProcessor);
                cloudConnectionProvider.BindEdgeHub(edgeHub);

                var versionInfo = new VersionInfo("v1", "b1", "c1");

                // Create Edge Hub connection
                EdgeHubConnection edgeHubConnection = await EdgeHubConnection.Create(
                    edgeHubCredentials.Identity,
                    edgeHub,
                    twinManager,
                    connectionManager,
                    routeFactory,
                    twinCollectionMessageConverter,
                    versionInfo,
                    new NullDeviceScopeIdentitiesCache());

                await Task.Delay(TimeSpan.FromMinutes(1));

                TwinConfigSource configSource = new TwinConfigSource(edgeHubConnection, edgeHubCredentials.Identity.Id, versionInfo, twinManager, twinMessageConverter, twinCollectionMessageConverter, routeFactory);

                // Get and Validate EdgeHubConfig
                Option <EdgeHubConfig> edgeHubConfigOption = await configSource.GetConfig();

                Assert.True(edgeHubConfigOption.HasValue);
                EdgeHubConfig edgeHubConfig = edgeHubConfigOption.OrDefault();
                Assert.Equal("1.0", edgeHubConfig.SchemaVersion);
                Assert.NotNull(edgeHubConfig.Routes);
                Assert.NotNull(edgeHubConfig.StoreAndForwardConfiguration);
                Assert.Equal(20, edgeHubConfig.StoreAndForwardConfiguration.TimeToLiveSecs);

                IReadOnlyDictionary <string, RouteConfig> routes = edgeHubConfig.Routes;
                Assert.Equal(4, routes.Count);

                RouteConfig route1 = routes["route1"];
                Assert.True(route1.Route.Endpoint.GetType() == typeof(CloudEndpoint));
                Assert.Equal("route1", route1.Name);
                Assert.Equal("from /* INTO $upstream", route1.Value);

                RouteConfig route2   = routes["route2"];
                Endpoint    endpoint = route2.Route.Endpoint;
                Assert.True(endpoint.GetType() == typeof(ModuleEndpoint));
                Assert.Equal($"{edgeDeviceId}/module2/input1", endpoint.Id);
                Assert.Equal("route2", route2.Name);
                Assert.Equal("from /modules/module1 INTO BrokeredEndpoint(\"/modules/module2/inputs/input1\")", route2.Value);

                RouteConfig route3 = routes["route3"];
                endpoint = route3.Route.Endpoint;
                Assert.True(endpoint.GetType() == typeof(ModuleEndpoint));
                Assert.Equal($"{edgeDeviceId}/module3/input1", endpoint.Id);
                Assert.Equal("route3", route3.Name);
                Assert.Equal("from /modules/module2 INTO BrokeredEndpoint(\"/modules/module3/inputs/input1\")", route3.Value);

                RouteConfig route4 = routes["route4"];
                endpoint = route4.Route.Endpoint;
                Assert.True(endpoint.GetType() == typeof(ModuleEndpoint));
                Assert.Equal($"{edgeDeviceId}/module4/input1", endpoint.Id);
                Assert.Equal("route4", route4.Name);
                Assert.Equal("from /modules/module3 INTO BrokeredEndpoint(\"/modules/module4/inputs/input1\")", route4.Value);

                // Make sure reported properties were updated appropriately
                EdgeHubConnection.ReportedProperties reportedProperties = await this.GetReportedProperties(registryManager, edgeDeviceId);

                Assert.Equal(200, reportedProperties.LastDesiredStatus.Code);
                Assert.NotNull(reportedProperties.Clients);
                Assert.Equal(0, reportedProperties.Clients.Count);
                Assert.Equal("1.0", reportedProperties.SchemaVersion);
                Assert.Equal(versionInfo, reportedProperties.VersionInfo);

                // Simulate a module and a downstream device that connects to Edge Hub.
                string             moduleId = "module1";
                string             sasToken = TokenHelper.CreateSasToken($"{iothubHostName}/devices/{edgeDeviceId}/modules/{moduleId}");
                string             moduleConnectionstring  = $"HostName={iothubHostName};DeviceId={edgeDeviceId};ModuleId={moduleId};SharedAccessSignature={sasToken}";
                IClientCredentials moduleClientCredentials = identityFactory.GetWithConnectionString(moduleConnectionstring);
                var moduleProxy = Mock.Of <IDeviceProxy>(d => d.IsActive);

                string downstreamDeviceId = "device1";
                sasToken = TokenHelper.CreateSasToken($"{iothubHostName}/devices/{downstreamDeviceId}");
                string             downstreamDeviceConnectionstring = $"HostName={iothubHostName};DeviceId={downstreamDeviceId};SharedAccessSignature={sasToken}";
                IClientCredentials downstreamDeviceCredentials      = identityFactory.GetWithConnectionString(downstreamDeviceConnectionstring);
                var downstreamDeviceProxy = Mock.Of <IDeviceProxy>(d => d.IsActive);

                // Connect the module and downstream device and make sure the reported properties are updated as expected.
                await connectionManager.AddDeviceConnection(moduleClientCredentials.Identity, moduleProxy);

                await connectionManager.AddDeviceConnection(downstreamDeviceCredentials.Identity, downstreamDeviceProxy);

                string moduleIdKey = $"{edgeDeviceId}/{moduleId}";
                await Task.Delay(TimeSpan.FromSeconds(10));

                reportedProperties = await this.GetReportedProperties(registryManager, edgeDeviceId);

                Assert.Equal(2, reportedProperties.Clients.Count);
                Assert.Equal(ConnectionStatus.Connected, reportedProperties.Clients[moduleIdKey].Status);
                Assert.NotNull(reportedProperties.Clients[moduleIdKey].LastConnectedTimeUtc);
                Assert.Null(reportedProperties.Clients[moduleIdKey].LastDisconnectTimeUtc);
                Assert.Equal(ConnectionStatus.Connected, reportedProperties.Clients[downstreamDeviceId].Status);
                Assert.NotNull(reportedProperties.Clients[downstreamDeviceId].LastConnectedTimeUtc);
                Assert.Null(reportedProperties.Clients[downstreamDeviceId].LastDisconnectTimeUtc);
                Assert.Equal(200, reportedProperties.LastDesiredStatus.Code);
                Assert.Equal("1.0", reportedProperties.SchemaVersion);
                Assert.Equal(versionInfo, reportedProperties.VersionInfo);

                // Update desired propertied and make sure callback is called with valid values
                bool callbackCalled = false;

                Task ConfigUpdatedCallback(EdgeHubConfig updatedConfig)
                {
                    Assert.NotNull(updatedConfig);
                    Assert.NotNull(updatedConfig.StoreAndForwardConfiguration);
                    Assert.NotNull(updatedConfig.Routes);

                    routes = updatedConfig.Routes;
                    Assert.Equal(4, routes.Count);

                    route1 = routes["route1"];
                    Assert.True(route1.Route.Endpoint.GetType() == typeof(CloudEndpoint));
                    Assert.Equal("route1", route1.Name);
                    Assert.Equal("from /* INTO $upstream", route1.Value);

                    route2   = routes["route2"];
                    endpoint = route2.Route.Endpoint;
                    Assert.True(endpoint.GetType() == typeof(ModuleEndpoint));
                    Assert.Equal($"{edgeDeviceId}/module2/input1", endpoint.Id);
                    Assert.Equal("route2", route2.Name);
                    Assert.Equal("from /modules/module1 INTO BrokeredEndpoint(\"/modules/module2/inputs/input1\")", route2.Value);

                    route3   = routes["route4"];
                    endpoint = route3.Route.Endpoint;
                    Assert.True(endpoint.GetType() == typeof(ModuleEndpoint));
                    Assert.Equal($"{edgeDeviceId}/module5/input1", endpoint.Id);
                    Assert.Equal("route4", route3.Name);
                    Assert.Equal("from /modules/module3 INTO BrokeredEndpoint(\"/modules/module5/inputs/input1\")", route3.Value);

                    route4   = routes["route5"];
                    endpoint = route4.Route.Endpoint;
                    Assert.True(endpoint.GetType() == typeof(ModuleEndpoint));
                    Assert.Equal($"{edgeDeviceId}/module6/input1", endpoint.Id);
                    Assert.Equal("route5", route4.Name);
                    Assert.Equal("from /modules/module5 INTO BrokeredEndpoint(\"/modules/module6/inputs/input1\")", route4.Value);

                    callbackCalled = true;
                    return(Task.CompletedTask);
                }

                configSource.SetConfigUpdatedCallback(ConfigUpdatedCallback);
                await this.UpdateDesiredProperties(registryManager, edgeDeviceId);

                await Task.Delay(TimeSpan.FromSeconds(5));

                Assert.True(callbackCalled);

                reportedProperties = await this.GetReportedProperties(registryManager, edgeDeviceId);

                Assert.Equal(200, reportedProperties.LastDesiredStatus.Code);
                Assert.NotNull(reportedProperties.Clients);
                Assert.Equal(2, reportedProperties.Clients.Count);
                Assert.Equal("1.0", reportedProperties.SchemaVersion);
                Assert.Equal(versionInfo, reportedProperties.VersionInfo);

                // Disconnect the downstream device and make sure the reported properties are updated as expected.
                await connectionManager.RemoveDeviceConnection(moduleIdKey);

                await connectionManager.RemoveDeviceConnection(downstreamDeviceId);

                await Task.Delay(TimeSpan.FromSeconds(10));

                reportedProperties = await this.GetReportedProperties(registryManager, edgeDeviceId);

                Assert.Equal(1, reportedProperties.Clients.Count);
                Assert.True(reportedProperties.Clients.ContainsKey(moduleIdKey));
                Assert.False(reportedProperties.Clients.ContainsKey(downstreamDeviceId));
                Assert.Equal(ConnectionStatus.Disconnected, reportedProperties.Clients[moduleIdKey].Status);
                Assert.NotNull(reportedProperties.Clients[moduleIdKey].LastConnectedTimeUtc);
                Assert.NotNull(reportedProperties.Clients[moduleIdKey].LastDisconnectTimeUtc);
                Assert.Equal(200, reportedProperties.LastDesiredStatus.Code);
                Assert.Equal("1.0", reportedProperties.SchemaVersion);
                Assert.Equal(versionInfo, reportedProperties.VersionInfo);

                // If the edge hub restarts, clear out the connected devices in the reported properties.
                await EdgeHubConnection.Create(
                    edgeHubCredentials.Identity,
                    edgeHub,
                    twinManager,
                    connectionManager,
                    routeFactory,
                    twinCollectionMessageConverter,
                    versionInfo,
                    new NullDeviceScopeIdentitiesCache());

                await Task.Delay(TimeSpan.FromMinutes(1));

                reportedProperties = await this.GetReportedProperties(registryManager, edgeDeviceId);

                Assert.Null(reportedProperties.Clients);
                Assert.Equal("1.0", reportedProperties.SchemaVersion);
                Assert.Equal(versionInfo, reportedProperties.VersionInfo);
            }
            finally
            {
                try
                {
                    await RegistryManagerHelper.RemoveDevice(edgeDeviceId, registryManager);
                }
                catch (Exception)
                {
                    // ignored
                }
            }
        }
        public async Task TestSendMessages()
        {
            // Arrange
            const string Id                             = "id1";
            var          identity                       = Mock.Of <IIdentity>(i => i.Id == Id);
            var          twinMessageConverter           = new TwinMessageConverter();
            var          twinCollectionMessageConverter = new TwinCollectionMessageConverter();
            var          messageConverterProvider       = new MessageConverterProvider(
                new Dictionary <Type, IMessageConverter>()
            {
                { typeof(Message), new DeviceClientMessageConverter() },
                { typeof(Shared.Twin), twinMessageConverter },
                { typeof(TwinCollection), twinCollectionMessageConverter }
            });

            var edgeHubTokenProvider = new Mock <ITokenProvider>();

            var clientWatcher = new ClientWatcher();

            var clientProvider = new Mock <IClientProvider>();

            clientProvider.Setup(c => c.Create(identity, edgeHubTokenProvider.Object, It.IsAny <ITransportSettings[]>(), Option.None <string>()))
            .Returns(() => new ThrowingClient(clientWatcher, 3));

            var deviceScopeIdentitiesCache = new Mock <IDeviceScopeIdentitiesCache>();

            deviceScopeIdentitiesCache.Setup(d => d.GetServiceIdentity(Id))
            .ReturnsAsync(
                Option.Some(
                    new ServiceIdentity(
                        Id,
                        "dummy",
                        new List <string>(),
                        new ServiceAuthentication(new SymmetricKeyAuthentication("foo", "bar")),
                        ServiceIdentityStatus.Enabled)));
            deviceScopeIdentitiesCache.Setup(d => d.GetAuthChain(It.Is <string>(i => i == Id)))
            .ReturnsAsync(Option.Some(Id));

            var edgeHubIdentity = Mock.Of <IIdentity>();

            ConnectionMetadata connectionMetadata = new ConnectionMetadata("edgeProdInfo");
            var metadataStore = new Mock <IMetadataStore>();

            metadataStore.Setup(p => p.GetMetadata(Id))
            .ReturnsAsync(connectionMetadata);

            var identityProvider = new Mock <IIdentityProvider>();

            identityProvider.Setup(i => i.Create(Id)).Returns(identity);

            var credentialsCache = new Mock <ICredentialsCache>();
            var edgeHub          = new Mock <IEdgeHub>();

            var connectionProvider = new CloudConnectionProvider(
                messageConverterProvider,
                1,
                clientProvider.Object,
                Option.None <UpstreamProtocol>(),
                edgeHubTokenProvider.Object,
                deviceScopeIdentitiesCache.Object,
                credentialsCache.Object,
                edgeHubIdentity,
                TimeSpan.FromMinutes(10),
                false,
                TimeSpan.FromMinutes(10),
                false,
                Option.None <IWebProxy>(),
                metadataStore.Object,
                scopeAuthenticationOnly: true,
                trackDeviceState: true,
                true);

            connectionProvider.BindEdgeHub(edgeHub.Object);

            var deviceConnectivityManager = Mock.Of <IDeviceConnectivityManager>();
            var connectionManager         = new ConnectionManager(connectionProvider, credentialsCache.Object, identityProvider.Object, deviceConnectivityManager);
            var messagesToSend            = new List <IMessage>();

            for (int i = 0; i < 10; i++)
            {
                var message = new EdgeMessage.Builder(new byte[i])
                              .SetSystemProperties(
                    new Dictionary <string, string>()
                {
                    [SystemProperties.MessageId] = i.ToString()
                })
                              .Build();
                messagesToSend.Add(message);
            }

            // Act
            Option <ICloudProxy> cloudProxyOption = await connectionManager.GetCloudConnection(Id);

            // Assert
            Assert.True(cloudProxyOption.HasValue);
            ICloudProxy cloudProxy = cloudProxyOption.OrDefault();

            Assert.True(cloudProxy.IsActive);

            // Act
            await RunSendMessages(cloudProxy, messagesToSend);

            // Assert
            Assert.Equal(messagesToSend.Count, clientWatcher.ReceivedMessages.Count());
            Assert.Equal(5, clientWatcher.OpenAsyncCount);
            Assert.True(cloudProxy.IsActive);

            IEnumerable <string> expectedMessageIds = messagesToSend.Select(m => m.SystemProperties[SystemProperties.MessageId]);
            IEnumerable <string> receivedMessageIds = clientWatcher.ReceivedMessages.Select(m => m.MessageId);

            Assert.Equal(expectedMessageIds, receivedMessageIds);
        }
        public async Task TestGetTwin()
        {
            // Arrange
            const string Id                             = "id1";
            var          identity                       = Mock.Of <IIdentity>(i => i.Id == Id);
            var          twinMessageConverter           = new TwinMessageConverter();
            var          twinCollectionMessageConverter = new TwinCollectionMessageConverter();
            var          messageConverterProvider       = new MessageConverterProvider(
                new Dictionary <Type, IMessageConverter>()
            {
                { typeof(Message), new DeviceClientMessageConverter() },
                { typeof(Shared.Twin), twinMessageConverter },
                { typeof(TwinCollection), twinCollectionMessageConverter }
            });

            var edgeHubTokenProvider = new Mock <ITokenProvider>();

            var clientWatcher = new ClientWatcher();

            var clientProvider = new Mock <IClientProvider>();

            clientProvider.Setup(c => c.Create(identity, edgeHubTokenProvider.Object, It.IsAny <ITransportSettings[]>(), Option.None <string>()))
            .Returns(() => new ThrowingClient(clientWatcher, 3));

            var deviceScopeIdentitiesCache = new Mock <IDeviceScopeIdentitiesCache>();

            deviceScopeIdentitiesCache.Setup(d => d.GetServiceIdentity(Id))
            .ReturnsAsync(
                Option.Some(
                    new ServiceIdentity(
                        Id,
                        "dummy",
                        new List <string>(),
                        new ServiceAuthentication(new SymmetricKeyAuthentication("foo", "bar")),
                        ServiceIdentityStatus.Enabled)));
            deviceScopeIdentitiesCache.Setup(d => d.GetAuthChain(It.Is <string>(i => i == Id)))
            .ReturnsAsync(Option.Some(Id));

            var edgeHubIdentity = Mock.Of <IIdentity>();

            ConnectionMetadata connectionMetadata = new ConnectionMetadata("edgeProdInfo");
            var metadataStore = new Mock <IMetadataStore>();

            metadataStore.Setup(p => p.GetMetadata(Id))
            .ReturnsAsync(connectionMetadata);

            var identityProvider = new Mock <IIdentityProvider>();

            identityProvider.Setup(i => i.Create(Id)).Returns(identity);

            var credentialsCache = new Mock <ICredentialsCache>();
            var edgeHub          = new Mock <IEdgeHub>();

            var connectionProvider = new CloudConnectionProvider(
                messageConverterProvider,
                1,
                clientProvider.Object,
                Option.None <UpstreamProtocol>(),
                edgeHubTokenProvider.Object,
                deviceScopeIdentitiesCache.Object,
                credentialsCache.Object,
                edgeHubIdentity,
                TimeSpan.FromMinutes(10),
                false,
                TimeSpan.FromMinutes(10),
                false,
                Option.None <IWebProxy>(),
                metadataStore.Object,
                true);

            connectionProvider.BindEdgeHub(edgeHub.Object);

            var deviceConnectivityManager = Mock.Of <IDeviceConnectivityManager>();
            var connectionManager         = new ConnectionManager(connectionProvider, credentialsCache.Object, identityProvider.Object, deviceConnectivityManager);

            // Act
            Option <ICloudProxy> cloudProxyOption = await connectionManager.GetCloudConnection(Id);

            // Assert
            Assert.True(cloudProxyOption.HasValue);
            ICloudProxy cloudProxy = cloudProxyOption.OrDefault();

            Assert.True(cloudProxy.IsActive);

            // Act
            await RunGetTwin(cloudProxy, 10);

            // Assert
            Assert.Equal(5, clientWatcher.OpenAsyncCount);
            Assert.True(cloudProxy.IsActive);
            Assert.Equal(10, clientWatcher.GetTwinCount);
        }
        public async Task TestMultipleOperations()
        {
            // Arrange
            const string Id                             = "id1";
            var          identity                       = Mock.Of <IIdentity>(i => i.Id == Id);
            var          twinMessageConverter           = new TwinMessageConverter();
            var          twinCollectionMessageConverter = new TwinCollectionMessageConverter();
            var          messageConverterProvider       = new MessageConverterProvider(
                new Dictionary <Type, IMessageConverter>
            {
                { typeof(Message), new DeviceClientMessageConverter() },
                { typeof(Shared.Twin), twinMessageConverter },
                { typeof(TwinCollection), twinCollectionMessageConverter }
            });

            var edgeHubTokenProvider = new Mock <ITokenProvider>();

            var clientWatcher = new ClientWatcher();

            var clientProvider = new Mock <IClientProvider>();

            clientProvider.Setup(c => c.Create(identity, edgeHubTokenProvider.Object, It.IsAny <ITransportSettings[]>()))
            .Returns(() => new ThrowingClient(clientWatcher, 15));

            var deviceScopeIdentitiesCache = new Mock <IDeviceScopeIdentitiesCache>();

            deviceScopeIdentitiesCache.Setup(d => d.GetServiceIdentity(Id, false))
            .ReturnsAsync(
                Option.Some(
                    new ServiceIdentity(
                        Id,
                        "dummy",
                        new List <string>(),
                        new ServiceAuthentication(new SymmetricKeyAuthentication("foo", "bar")),
                        ServiceIdentityStatus.Enabled)));

            var edgeHubIdentity = Mock.Of <IIdentity>();

            var productInfoStore = new Mock <IProductInfoStore>();

            productInfoStore.Setup(p => p.GetEdgeProductInfo(Id))
            .ReturnsAsync("ProdInfo1");

            var identityProvider = new Mock <IIdentityProvider>();

            identityProvider.Setup(i => i.Create(Id)).Returns(identity);

            var credentialsCache = new Mock <ICredentialsCache>();
            var edgeHub          = new Mock <IEdgeHub>();

            var connectionProvider = new CloudConnectionProvider(
                messageConverterProvider,
                1,
                clientProvider.Object,
                Option.None <UpstreamProtocol>(),
                edgeHubTokenProvider.Object,
                deviceScopeIdentitiesCache.Object,
                credentialsCache.Object,
                edgeHubIdentity,
                TimeSpan.FromMinutes(10),
                false,
                TimeSpan.FromMinutes(10),
                Option.None <IWebProxy>(),
                productInfoStore.Object);

            connectionProvider.BindEdgeHub(edgeHub.Object);

            var connectionManager = new ConnectionManager(connectionProvider, credentialsCache.Object, identityProvider.Object);

            async Task <ICloudProxy> GetCloudProxy(IConnectionManager cm)
            {
                // Act
                Option <ICloudProxy> cloudProxyOption = await cm.GetCloudConnection(Id);

                // Assert
                Assert.True(cloudProxyOption.HasValue);
                ICloudProxy cloudProxy = cloudProxyOption.OrDefault();

                Assert.True(cloudProxy.IsActive);
                return(cloudProxy);
            }

            var messagesToSend = new List <IMessage>();

            for (int i = 0; i < 60; i++)
            {
                var message = new EdgeMessage.Builder(new byte[i])
                              .SetSystemProperties(
                    new Dictionary <string, string>()
                {
                    [SystemProperties.MessageId] = i.ToString()
                })
                              .Build();
                messagesToSend.Add(message);
            }

            // Act
            ICloudProxy cloudProxy1 = await GetCloudProxy(connectionManager);

            ICloudProxy cloudProxy2 = await GetCloudProxy(connectionManager);

            ICloudProxy cloudProxy3 = await GetCloudProxy(connectionManager);

            // Act
            var tasks = new[]
            {
                RunGetTwin(cloudProxy1, 10),
                RunGetTwin(cloudProxy2, 30),
                RunGetTwin(cloudProxy3, 10),
                RunSendMessages(cloudProxy1, messagesToSend.Take(20), 2),
                RunSendMessages(cloudProxy2, messagesToSend.Skip(20).Take(10)),
                RunSendMessages(cloudProxy3, messagesToSend.Skip(30).Take(30), 3)
            };
            await Task.WhenAll(tasks);

            // Assert
            Assert.Equal(50, clientWatcher.GetTwinCount);
            List <string> expectedMessageIds = messagesToSend
                                               .Select(m => m.SystemProperties[SystemProperties.MessageId])
                                               .OrderBy(s => s)
                                               .ToList();
            List <string> receivedMessageIds = clientWatcher.ReceivedMessages
                                               .Select(m => m.MessageId)
                                               .OrderBy(s => s)
                                               .ToList();

            Assert.Equal(expectedMessageIds.Count, receivedMessageIds.Count);
            Assert.Equal(expectedMessageIds, receivedMessageIds);
        }
        public async Task DeviceConnectionSyncPeriodTest()
        {
            string id       = "d1";
            var    identity = Mock.Of <IIdentity>(i => i.Id == id);

            var desired2 = new TwinCollection
            {
                ["p1"]       = "vp1",
                ["p2"]       = "v2",
                ["p3"]       = "v3",
                ["$version"] = 2
            };

            var reported2 = new TwinCollection
            {
                ["p2"]       = "vp2",
                ["$version"] = 2
            };

            var twin2 = new Twin
            {
                Properties = new TwinProperties
                {
                    Reported = reported2,
                    Desired  = desired2
                }
            };

            var twinStore = new Mock <ITwinStore>(MockBehavior.Strict);

            twinStore.Setup(c => c.Get(id))
            .ReturnsAsync(Option.Some(twin2));

            Twin storedTwin = null;

            twinStore.Setup(c => c.Update(id, It.IsAny <Twin>()))
            .Callback <string, Twin>((s, t) => storedTwin = t)
            .Returns(Task.CompletedTask);

            var reportedPropertiesStore = new Mock <IReportedPropertiesStore>(MockBehavior.Strict);

            reportedPropertiesStore.Setup(r => r.SyncToCloud(id))
            .Returns(Task.CompletedTask);

            var deviceProxy = new Mock <IDeviceProxy>(MockBehavior.Strict);

            var cloudSync = new Mock <ICloudSync>(MockBehavior.Strict);

            cloudSync.Setup(c => c.GetTwin(id))
            .ReturnsAsync(Option.Some(twin2));
            var twinMessageConverter        = new TwinMessageConverter();
            var twinCollectionConverter     = new TwinCollectionMessageConverter();
            var reportedPropertiesValidator = Mock.Of <IValidator <TwinCollection> >();

            var connectionManager = Mock.Of <IConnectionManager>(
                c =>
                c.CheckClientSubscription(id, DeviceSubscription.DesiredPropertyUpdates) &&
                c.GetDeviceConnection(id) == Option.Some(deviceProxy.Object) &&
                c.GetConnectedClients() == new[] { identity });

            var deviceConnectivityManager = new Mock <IDeviceConnectivityManager>();

            var twinManager = new StoringTwinManager(
                connectionManager,
                twinCollectionConverter,
                twinMessageConverter,
                reportedPropertiesValidator,
                twinStore.Object,
                reportedPropertiesStore.Object,
                cloudSync.Object,
                deviceConnectivityManager.Object,
                TimeSpan.FromMinutes(10));

            // Act
            IMessage getTwinMessage = await twinManager.GetTwinAsync(id);

            // Assert
            Assert.NotNull(getTwinMessage);
            Twin getTwin = twinMessageConverter.FromMessage(getTwinMessage);

            Assert.NotNull(getTwin);
            Assert.Equal(twin2.ToJson(), getTwin.ToJson());

            // Act
            deviceConnectivityManager.Raise(d => d.DeviceConnected += null, this, new EventArgs());

            // Assert
            await Task.Delay(TimeSpan.FromSeconds(3));

            twinStore.VerifyAll();
            reportedPropertiesStore.VerifyAll();
            deviceProxy.VerifyAll();
            cloudSync.Verify(c => c.GetTwin(id), Times.AtMostOnce);

            Assert.NotNull(storedTwin);
            Assert.Equal(twin2.ToJson(), storedTwin.ToJson());
        }
        public async Task UpdateDesiredPropertiesWithIncorrectPatchTest()
        {
            string id = "d1";

            var desired0 = new TwinCollection
            {
                ["p0"]       = "vp0",
                ["$version"] = 0
            };

            var reported0 = new TwinCollection
            {
                ["p0"]       = "vp0",
                ["$version"] = 0
            };

            var twinBase = new Twin
            {
                Properties = new TwinProperties
                {
                    Reported = reported0,
                    Desired  = desired0
                }
            };

            var desired2 = new TwinCollection
            {
                ["p1"]       = "vp1",
                ["p2"]       = "v2",
                ["p3"]       = "v3",
                ["$version"] = 2
            };

            var reported2 = new TwinCollection
            {
                ["p2"]       = "vp2",
                ["$version"] = 2
            };

            var twin2 = new Twin
            {
                Properties = new TwinProperties
                {
                    Reported = reported2,
                    Desired  = desired2
                }
            };

            var desired2Patch = new TwinCollection
            {
                ["p1"]       = "vp1",
                ["$version"] = 2
            };

            var twinStore = new Mock <ITwinStore>(MockBehavior.Strict);

            twinStore.Setup(c => c.Get(id))
            .ReturnsAsync(Option.Some(twinBase));

            Twin storedTwin = null;

            twinStore.Setup(c => c.Update(id, It.IsAny <Twin>()))
            .Callback <string, Twin>((s, t) => storedTwin = t)
            .Returns(Task.CompletedTask);

            var reportedPropertiesStore = new Mock <IReportedPropertiesStore>(MockBehavior.Strict);

            IMessage receivedTwinPatchMessage = null;
            var      deviceProxy = new Mock <IDeviceProxy>();

            deviceProxy.Setup(d => d.OnDesiredPropertyUpdates(It.IsAny <IMessage>()))
            .Callback <IMessage>(m => receivedTwinPatchMessage = m)
            .Returns(Task.CompletedTask);

            var cloudSync                   = Mock.Of <ICloudSync>(c => c.GetTwin(id) == Task.FromResult(Option.Some(twin2)));
            var twinMessageConverter        = new TwinMessageConverter();
            var twinCollectionConverter     = new TwinCollectionMessageConverter();
            var reportedPropertiesValidator = Mock.Of <IValidator <TwinCollection> >();
            var deviceConnectivityManager   = Mock.Of <IDeviceConnectivityManager>();

            var connectionManager = Mock.Of <IConnectionManager>(
                c =>
                c.CheckClientSubscription(id, DeviceSubscription.DesiredPropertyUpdates) &&
                c.GetDeviceConnection(id) == Option.Some(deviceProxy.Object));

            var twinManager = new StoringTwinManager(
                connectionManager,
                twinCollectionConverter,
                twinMessageConverter,
                reportedPropertiesValidator,
                twinStore.Object,
                reportedPropertiesStore.Object,
                cloudSync,
                deviceConnectivityManager,
                TimeSpan.FromMinutes(10));

            IMessage desiredPropertiesMessage = twinCollectionConverter.ToMessage(desired2Patch);

            // Act
            await twinManager.UpdateDesiredPropertiesAsync(id, desiredPropertiesMessage);

            // Assert
            twinStore.VerifyAll();
            reportedPropertiesStore.VerifyAll();

            Assert.NotNull(storedTwin);
            Assert.NotNull(receivedTwinPatchMessage);
            Assert.Equal(twin2.ToJson(), storedTwin.ToJson());
            TwinCollection receivedTwinPatch2 = twinCollectionConverter.FromMessage(receivedTwinPatchMessage);

            Assert.Equal("{\"p0\":null,\"$version\":2,\"p1\":\"vp1\",\"p2\":\"v2\",\"p3\":\"v3\"}", receivedTwinPatch2.ToJson());
        }