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();
        }
Example #3
0
        protected override void Load(ContainerBuilder builder)
        {
            // IMessageConverter<IRoutingMessage>
            builder.Register(c => new RoutingMessageConverter())
            .As <Core.IMessageConverter <IRoutingMessage> >()
            .SingleInstance();

            // IRoutingPerfCounter
            builder.Register(
                c =>
            {
                Routing.PerfCounter = NullRoutingPerfCounter.Instance;
                return(Routing.PerfCounter);
            })
            .As <IRoutingPerfCounter>()
            .AutoActivate()
            .SingleInstance();

            // IRoutingUserAnalyticsLogger
            builder.Register(
                c =>
            {
                Routing.UserAnalyticsLogger = NullUserAnalyticsLogger.Instance;
                return(Routing.UserAnalyticsLogger);
            })
            .As <IRoutingUserAnalyticsLogger>()
            .AutoActivate()
            .SingleInstance();

            // IRoutingUserMetricLogger
            builder.Register(
                c =>
            {
                Routing.UserMetricLogger = NullRoutingUserMetricLogger.Instance;
                return(Routing.UserMetricLogger);
            })
            .As <IRoutingUserMetricLogger>()
            .AutoActivate()
            .SingleInstance();

            // IMessageConverter<Message>
            builder.Register(c => new DeviceClientMessageConverter())
            .As <Core.IMessageConverter <Message> >()
            .SingleInstance();

            // IMessageConverter<Twin>
            builder.Register(c => new TwinMessageConverter())
            .As <Core.IMessageConverter <Twin> >()
            .SingleInstance();

            // IMessageConverter<TwinCollection>
            builder.Register(c => new TwinCollectionMessageConverter())
            .As <Core.IMessageConverter <TwinCollection> >()
            .SingleInstance();

            // IMessageConverterProvider
            builder.Register(
                c => new MessageConverterProvider(
                    new Dictionary <Type, IMessageConverter>()
            {
                { typeof(Message), c.Resolve <Core.IMessageConverter <Message> >() },
                { typeof(Twin), c.Resolve <Core.IMessageConverter <Twin> >() },
                { typeof(TwinCollection), c.Resolve <Core.IMessageConverter <TwinCollection> >() }
            }))
            .As <IMessageConverterProvider>()
            .SingleInstance();

            // IDeviceConnectivityManager
            builder.Register(
                c =>
            {
                var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials");
                IDeviceConnectivityManager deviceConnectivityManager = this.experimentalFeatures.DisableConnectivityCheck
                            ? new NullDeviceConnectivityManager()
                            : new DeviceConnectivityManager(this.connectivityCheckFrequency, TimeSpan.FromMinutes(2), edgeHubCredentials.Identity) as IDeviceConnectivityManager;
                return(deviceConnectivityManager);
            })
            .As <IDeviceConnectivityManager>()
            .SingleInstance();

            // IDeviceClientProvider
            builder.Register(
                c =>
            {
                IClientProvider underlyingClientProvider        = new ClientProvider();
                IClientProvider connectivityAwareClientProvider = new ConnectivityAwareClientProvider(underlyingClientProvider, c.Resolve <IDeviceConnectivityManager>());
                return(connectivityAwareClientProvider);
            })
            .As <IClientProvider>()
            .SingleInstance();

            // Task<ICloudConnectionProvider>
            builder.Register(
                async c =>
            {
                var productInfoStore               = await c.Resolve <Task <IProductInfoStore> >();
                var messageConverterProvider       = c.Resolve <IMessageConverterProvider>();
                var clientProvider                 = c.Resolve <IClientProvider>();
                var tokenProvider                  = c.ResolveNamed <ITokenProvider>("EdgeHubClientAuthTokenProvider");
                var credentialsCacheTask           = c.Resolve <Task <ICredentialsCache> >();
                var edgeHubCredentials             = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials");
                var deviceScopeIdentitiesCacheTask = c.Resolve <Task <IDeviceScopeIdentitiesCache> >();
                var proxy = c.Resolve <Option <IWebProxy> >();
                IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await deviceScopeIdentitiesCacheTask;
                ICredentialsCache credentialsCache = await credentialsCacheTask;
                ICloudConnectionProvider cloudConnectionProvider = new CloudConnectionProvider(
                    messageConverterProvider,
                    this.connectionPoolSize,
                    clientProvider,
                    this.upstreamProtocol,
                    tokenProvider,
                    deviceScopeIdentitiesCache,
                    credentialsCache,
                    edgeHubCredentials.Identity,
                    this.cloudConnectionIdleTimeout,
                    this.closeCloudConnectionOnIdleTimeout,
                    this.operationTimeout,
                    this.useServerHeartbeat,
                    proxy,
                    productInfoStore);
                return(cloudConnectionProvider);
            })
            .As <Task <ICloudConnectionProvider> >()
            .SingleInstance();

            // IIdentityProvider
            builder.Register(_ => new IdentityProvider(this.iotHubName))
            .As <IIdentityProvider>()
            .SingleInstance();

            // Task<IConnectionManager>
            builder.Register(
                async c =>
            {
                var cloudConnectionProviderTask = c.Resolve <Task <ICloudConnectionProvider> >();
                var credentialsCacheTask        = c.Resolve <Task <ICredentialsCache> >();
                var identityProvider            = c.Resolve <IIdentityProvider>();
                var deviceConnectivityManager   = c.Resolve <IDeviceConnectivityManager>();
                ICloudConnectionProvider cloudConnectionProvider = await cloudConnectionProviderTask;
                ICredentialsCache credentialsCache   = await credentialsCacheTask;
                IConnectionManager connectionManager = new ConnectionManager(
                    cloudConnectionProvider,
                    credentialsCache,
                    identityProvider,
                    deviceConnectivityManager,
                    this.maxConnectedClients,
                    this.closeCloudConnectionOnDeviceDisconnect);
                return(connectionManager);
            })
            .As <Task <IConnectionManager> >()
            .SingleInstance();

            // Task<IEndpointFactory>
            builder.Register(
                async c =>
            {
                var messageConverter = c.Resolve <Core.IMessageConverter <IRoutingMessage> >();
                IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >();
                return(new EndpointFactory(connectionManager, messageConverter, this.edgeDeviceId, this.maxUpstreamBatchSize, this.upstreamFanOutFactor) as IEndpointFactory);
            })
            .As <Task <IEndpointFactory> >()
            .SingleInstance();

            // Task<RouteFactory>
            builder.Register(async c => new EdgeRouteFactory(await c.Resolve <Task <IEndpointFactory> >()) as RouteFactory)
            .As <Task <RouteFactory> >()
            .SingleInstance();

            // RouterConfig
            builder.Register(c => new RouterConfig(Enumerable.Empty <Route>()))
            .As <RouterConfig>()
            .SingleInstance();

            if (!this.isStoreAndForwardEnabled)
            {
                // EndpointExecutorConfig
                builder.Register(
                    c =>
                {
                    RetryStrategy defaultRetryStrategy = new FixedInterval(0, TimeSpan.FromSeconds(1));
                    TimeSpan defaultRevivePeriod       = TimeSpan.FromHours(1);
                    TimeSpan defaultTimeout            = TimeSpan.FromSeconds(60);
                    return(new EndpointExecutorConfig(defaultTimeout, defaultRetryStrategy, defaultRevivePeriod, true));
                })
                .As <EndpointExecutorConfig>()
                .SingleInstance();

                // IEndpointExecutorFactory
                builder.Register(c => new SyncEndpointExecutorFactory(c.Resolve <EndpointExecutorConfig>()))
                .As <IEndpointExecutorFactory>()
                .SingleInstance();

                // Task<Router>
                builder.Register(
                    async c =>
                {
                    var endpointExecutorFactory = c.Resolve <IEndpointExecutorFactory>();
                    var routerConfig            = c.Resolve <RouterConfig>();
                    Router router = await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory);
                    return(router);
                })
                .As <Task <Router> >()
                .SingleInstance();

                // Task<ITwinManager>
                builder.Register(
                    async c =>
                {
                    if (!this.useV1TwinManager)
                    {
                        var messageConverterProvider         = c.Resolve <IMessageConverterProvider>();
                        IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >();
                        ITwinManager twinManager             = new PassThroughTwinManager(connectionManager, messageConverterProvider);
                        return(twinManager);
                    }
                    else
                    {
                        var messageConverterProvider         = c.Resolve <IMessageConverterProvider>();
                        IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >();
                        return(TwinManager.CreateTwinManager(connectionManager, messageConverterProvider, Option.None <IStoreProvider>()));
                    }
                })
                .As <Task <ITwinManager> >()
                .SingleInstance();
            }
            else
            {
                // EndpointExecutorConfig
                builder.Register(
                    c =>
                {
                    // Endpoint executor config values -
                    // ExponentialBackoff - minBackoff = 1s, maxBackoff = 60s, delta (used to add randomness to backoff) - 1s (default)
                    // Num of retries = int.MaxValue(we want to keep retrying till the message is sent)
                    // Revive period - period for which the endpoint should be considered dead if it doesn't respond - 1 min (we want to try continuously till the message expires)
                    // Timeout - time for which we want for the ack from the endpoint = 30s
                    // TODO - Should the number of retries be tied to the Store and Forward ttl? Not
                    // doing that right now as that value can be changed at runtime, but these settings
                    // cannot. Need to make the number of retries dynamically configurable for that.
                    TimeSpan minWait            = TimeSpan.FromSeconds(1);
                    TimeSpan maxWait            = TimeSpan.FromSeconds(60);
                    TimeSpan delta              = TimeSpan.FromSeconds(1);
                    int retries                 = int.MaxValue;
                    RetryStrategy retryStrategy = new ExponentialBackoff(retries, minWait, maxWait, delta);
                    TimeSpan timeout            = TimeSpan.FromSeconds(30);
                    TimeSpan revivePeriod       = TimeSpan.FromSeconds(30);
                    return(new EndpointExecutorConfig(timeout, retryStrategy, revivePeriod));
                })
                .As <EndpointExecutorConfig>()
                .SingleInstance();

                // Task<ICheckpointStore>
                builder.Register(
                    async c =>
                {
                    var dbStoreProvider              = await c.Resolve <Task <IDbStoreProvider> >();
                    IStoreProvider storeProvider     = new StoreProvider(dbStoreProvider);
                    ICheckpointStore checkpointStore = CheckpointStore.Create(storeProvider);
                    return(checkpointStore);
                })
                .As <Task <ICheckpointStore> >()
                .SingleInstance();

                // Task<IMessageStore>
                builder.Register(
                    async c =>
                {
                    var checkpointStore          = await c.Resolve <Task <ICheckpointStore> >();
                    var dbStoreProvider          = await c.Resolve <Task <IDbStoreProvider> >();
                    IStoreProvider storeProvider = new StoreProvider(dbStoreProvider);
                    IMessageStore messageStore   = new MessageStore(storeProvider, checkpointStore, this.storeAndForwardConfiguration.TimeToLive, this.checkEntireQueueOnCleanup);
                    return(messageStore);
                })
                .As <Task <IMessageStore> >()
                .SingleInstance();

                // Task<IEndpointExecutorFactory>
                builder.Register(
                    async c =>
                {
                    var endpointExecutorConfig = c.Resolve <EndpointExecutorConfig>();
                    var messageStore           = await c.Resolve <Task <IMessageStore> >();
                    IEndpointExecutorFactory endpointExecutorFactory = new StoringAsyncEndpointExecutorFactory(endpointExecutorConfig, new AsyncEndpointExecutorOptions(10, TimeSpan.FromSeconds(10)), messageStore);
                    return(endpointExecutorFactory);
                })
                .As <Task <IEndpointExecutorFactory> >()
                .SingleInstance();

                // Task<Router>
                builder.Register(
                    async c =>
                {
                    var checkpointStore         = await c.Resolve <Task <ICheckpointStore> >();
                    var routerConfig            = c.Resolve <RouterConfig>();
                    var endpointExecutorFactory = await c.Resolve <Task <IEndpointExecutorFactory> >();
                    return(await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory, checkpointStore));
                })
                .As <Task <Router> >()
                .SingleInstance();

                // Task<ITwinManager>
                builder.Register(
                    async c =>
                {
                    if (this.useV1TwinManager)
                    {
                        var dbStoreProvider                  = await c.Resolve <Task <IDbStoreProvider> >();
                        var messageConverterProvider         = c.Resolve <IMessageConverterProvider>();
                        IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >();
                        return(TwinManager.CreateTwinManager(connectionManager, messageConverterProvider, Option.Some <IStoreProvider>(new StoreProvider(dbStoreProvider))));
                    }
                    else
                    {
                        var messageConverterProvider  = c.Resolve <IMessageConverterProvider>();
                        var deviceConnectivityManager = c.Resolve <IDeviceConnectivityManager>();
                        var connectionManagerTask     = c.Resolve <Task <IConnectionManager> >();
                        IEntityStore <string, TwinStoreEntity> entityStore = await this.GetTwinStore(c);
                        IConnectionManager connectionManager = await connectionManagerTask;
                        ITwinManager twinManager             = StoringTwinManager.Create(
                            connectionManager,
                            messageConverterProvider,
                            entityStore,
                            deviceConnectivityManager,
                            new ReportedPropertiesValidator(),
                            this.minTwinSyncPeriod,
                            this.reportedPropertiesSyncFrequency);
                        return(twinManager);
                    }
                })
                .As <Task <ITwinManager> >()
                .SingleInstance();
            }

            // IClientCredentials "EdgeHubCredentials"
            builder.Register(
                c =>
            {
                var identityFactory = c.Resolve <IClientCredentialsFactory>();
                IClientCredentials edgeHubCredentials = this.connectionString.Map(cs => identityFactory.GetWithConnectionString(cs)).GetOrElse(
                    () => identityFactory.GetWithIotEdged(this.edgeDeviceId, this.edgeModuleId));
                return(edgeHubCredentials);
            })
            .Named <IClientCredentials>("EdgeHubCredentials")
            .SingleInstance();

            // Task<IInvokeMethodHandler>
            builder.Register(
                async c =>
            {
                IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >();
                return(new InvokeMethodHandler(connectionManager) as IInvokeMethodHandler);
            })
            .As <Task <IInvokeMethodHandler> >()
            .SingleInstance();

            // Task<ISubscriptionProcessor>
            builder.Register(
                async c =>
            {
                var connectionManagerTask = c.Resolve <Task <IConnectionManager> >();
                if (this.experimentalFeatures.DisableCloudSubscriptions)
                {
                    return(new LocalSubscriptionProcessor(await connectionManagerTask) as ISubscriptionProcessor);
                }
                else
                {
                    var invokeMethodHandlerTask              = c.Resolve <Task <IInvokeMethodHandler> >();
                    var deviceConnectivityManager            = c.Resolve <IDeviceConnectivityManager>();
                    IConnectionManager connectionManager     = await connectionManagerTask;
                    IInvokeMethodHandler invokeMethodHandler = await invokeMethodHandlerTask;
                    return(new SubscriptionProcessor(connectionManager, invokeMethodHandler, deviceConnectivityManager) as ISubscriptionProcessor);
                }
            })
            .As <Task <ISubscriptionProcessor> >()
            .SingleInstance();

            // Task<IEdgeHub>
            builder.Register(
                async c =>
            {
                var routingMessageConverter = c.Resolve <Core.IMessageConverter <IRoutingMessage> >();
                var routerTask                = c.Resolve <Task <Router> >();
                var twinManagerTask           = c.Resolve <Task <ITwinManager> >();
                var invokeMethodHandlerTask   = c.Resolve <Task <IInvokeMethodHandler> >();
                var connectionManagerTask     = c.Resolve <Task <IConnectionManager> >();
                var subscriptionProcessorTask = c.Resolve <Task <ISubscriptionProcessor> >();
                Router router                                = await routerTask;
                ITwinManager twinManager                     = await twinManagerTask;
                IConnectionManager connectionManager         = await connectionManagerTask;
                IInvokeMethodHandler invokeMethodHandler     = await invokeMethodHandlerTask;
                ISubscriptionProcessor subscriptionProcessor = await subscriptionProcessorTask;
                IEdgeHub hub = new RoutingEdgeHub(
                    router,
                    routingMessageConverter,
                    connectionManager,
                    twinManager,
                    this.edgeDeviceId,
                    invokeMethodHandler,
                    subscriptionProcessor);
                return(hub);
            })
            .As <Task <IEdgeHub> >()
            .SingleInstance();

            // Task<ConfigUpdater>
            builder.Register(
                async c =>
            {
                IMessageStore messageStore = this.isStoreAndForwardEnabled ? await c.Resolve <Task <IMessageStore> >() : null;
                var storageSpaceChecker    = c.Resolve <IStorageSpaceChecker>();
                var edgeHubCredentials     = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials");
                RouteFactory routeFactory  = await c.Resolve <Task <RouteFactory> >();
                Router router            = await c.Resolve <Task <Router> >();
                var twinManagerTask      = c.Resolve <Task <ITwinManager> >();
                var twinMessageConverter = c.Resolve <Core.IMessageConverter <Twin> >();
                var twinManager          = await twinManagerTask;
                var configUpdater        = new ConfigUpdater(router, messageStore, this.configUpdateFrequency, storageSpaceChecker);
                return(configUpdater);
            })
            .As <Task <ConfigUpdater> >()
            .SingleInstance();

            // Task<IConfigSource>
            builder.Register <Task <IConfigSource> >(
                async c =>
            {
                RouteFactory routeFactory = await c.Resolve <Task <RouteFactory> >();
                if (this.useTwinConfig)
                {
                    var edgeHubCredentials             = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials");
                    var twinCollectionMessageConverter = c.Resolve <Core.IMessageConverter <TwinCollection> >();
                    var twinMessageConverter           = c.Resolve <Core.IMessageConverter <Twin> >();
                    var twinManagerTask                  = c.Resolve <Task <ITwinManager> >();
                    var edgeHubTask                      = c.Resolve <Task <IEdgeHub> >();
                    ITwinManager twinManager             = await twinManagerTask;
                    IEdgeHub edgeHub                     = await edgeHubTask;
                    IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >();
                    IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await c.Resolve <Task <IDeviceScopeIdentitiesCache> >();

                    var edgeHubConnection = await EdgeHubConnection.Create(
                        edgeHubCredentials.Identity,
                        edgeHub,
                        twinManager,
                        connectionManager,
                        routeFactory,
                        twinCollectionMessageConverter,
                        this.versionInfo,
                        deviceScopeIdentitiesCache);

                    return(new TwinConfigSource(edgeHubConnection, edgeHubCredentials.Identity.Id, this.versionInfo, twinManager, twinMessageConverter, twinCollectionMessageConverter, routeFactory));
                }
                else
                {
                    return(new LocalConfigSource(routeFactory, this.routes, this.storeAndForwardConfiguration));
                }
            })
            .As <Task <IConfigSource> >()
            .SingleInstance();

            // Task<IConnectionProvider>
            builder.Register(
                async c =>
            {
                var connectionManagerTask              = c.Resolve <Task <IConnectionManager> >();
                var edgeHubTask                        = c.Resolve <Task <IEdgeHub> >();
                IConnectionManager connectionManager   = await connectionManagerTask;
                IEdgeHub edgeHub                       = await edgeHubTask;
                IConnectionProvider connectionProvider = new ConnectionProvider(connectionManager, edgeHub, this.messageAckTimeout);
                return(connectionProvider);
            })
            .As <Task <IConnectionProvider> >()
            .SingleInstance();

            base.Load(builder);
        }
        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());
        }
        public async Task GetTwinTest()
        {
            // Arrange
            string id = "d1";

            var desired1 = new TwinCollection
            {
                ["p1"]       = "vp1",
                ["p3"]       = "v3",
                ["$version"] = 1
            };

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

            var twin1 = new Twin
            {
                Properties = new TwinProperties
                {
                    Desired  = desired1,
                    Reported = reported1
                }
            };

            var cloudSync = new Mock <ICloudSync>();

            cloudSync.SetupSequence(c => c.GetTwin(id))
            .ReturnsAsync(Option.None <Twin>())
            .ReturnsAsync(Option.Some(twin1))
            .ReturnsAsync(Option.None <Twin>());

            Twin receivedTwin = null;
            var  twinStore    = new Mock <ITwinStore>();

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

            twinStore.Setup(c => c.Get(id))
            .ReturnsAsync(() => Option.Maybe(receivedTwin));

            var twinMessageConverter        = new TwinMessageConverter();
            var connectionManager           = Mock.Of <IConnectionManager>();
            var twinCollectionConverter     = Mock.Of <IMessageConverter <TwinCollection> >();
            var reportedPropertiesValidator = Mock.Of <IValidator <TwinCollection> >();
            var reportedPropertiesStore     = Mock.Of <IReportedPropertiesStore>();
            var deviceConnectivityManager   = Mock.Of <IDeviceConnectivityManager>();

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

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

            // Assert
            Assert.NotNull(twinMessage);
            Twin twin = twinMessageConverter.FromMessage(twinMessage);

            Assert.NotNull(twin);
            Assert.Equal("{\"deviceId\":null,\"etag\":null,\"version\":null,\"properties\":{\"desired\":{},\"reported\":{}}}", twin.ToJson());

            // Act
            twinMessage = await twinManager.GetTwinAsync(id);

            // Assert
            Assert.NotNull(twinMessage);
            twin = twinMessageConverter.FromMessage(twinMessage);
            Assert.NotNull(twin);
            Assert.NotNull(receivedTwin);
            Assert.Equal(receivedTwin.ToJson(), twin.ToJson());
            Assert.Equal(twin1.ToJson(), twin.ToJson());

            // Act
            twinMessage = await twinManager.GetTwinAsync(id);

            // Assert
            Assert.NotNull(twinMessage);
            twin = twinMessageConverter.FromMessage(twinMessage);
            Assert.NotNull(twin);
            Assert.NotNull(receivedTwin);
            Assert.Equal(receivedTwin.ToJson(), twin.ToJson());
            Assert.Equal(twin1.ToJson(), twin.ToJson());
        }