public async Task NoEventsTest() { // Arrange / act var deviceConnectivityManager = new DeviceConnectivityManager(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); var client = new Mock <IClient>(); client.SetupSequence(c => c.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>())) .Returns(Task.CompletedTask) .Throws <TimeoutException>() .Throws <TimeoutException>() .Returns(Task.CompletedTask); IClient connectivityAwareClient = new ConnectivityAwareClient(client.Object, deviceConnectivityManager); var cloudProxy = new CloudProxy(connectivityAwareClient, Mock.Of <IMessageConverterProvider>(), "d1/m1", null, Mock.Of <ICloudListener>(), TimeSpan.FromHours(1)); deviceConnectivityManager.SetTestCloudProxy(cloudProxy); bool connected = false; deviceConnectivityManager.DeviceConnected += (_, __) => connected = true; deviceConnectivityManager.DeviceDisconnected += (_, __) => connected = false; // Assert await Task.Delay(TimeSpan.FromSeconds(4)); Assert.True(connected); await Task.Delay(TimeSpan.FromSeconds(4)); Assert.False(connected); await Task.Delay(TimeSpan.FromSeconds(4)); Assert.True(connected); }
public async Task DisableHandlingEventsOnCloseTest() { // Arrange int connectionStatusChangedHandlerCount = 0; void ConnectionStatusChangedHandler(ConnectionStatus status, ConnectionStatusChangeReason reason) { Interlocked.Increment(ref connectionStatusChangedHandlerCount); } var deviceConnectivityManager = new DeviceConnectivityManager(); var client = Mock.Of <IClient>(); var connectivityAwareClient = new ConnectivityAwareClient(client, deviceConnectivityManager); connectivityAwareClient.SetConnectionStatusChangedHandler(ConnectionStatusChangedHandler); // Act deviceConnectivityManager.InvokeDeviceConnected(); deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(2, connectionStatusChangedHandlerCount); // Act await connectivityAwareClient.CloseAsync(); deviceConnectivityManager.InvokeDeviceConnected(); deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(2, connectionStatusChangedHandlerCount); }
public async Task ConnectivityTestFailedTest() { // Arrange / act var deviceIdentity = Mock.Of <IIdentity>(i => i.Id == "d2"); var edgeHubIdentity = Mock.Of <IIdentity>(i => i.Id == "d1/m1"); var deviceConnectivityManager = new DeviceConnectivityManager(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), edgeHubIdentity); var client = new Mock <IClient>(); client.SetupSequence(c => c.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>())) .Returns(Task.CompletedTask) .Throws <TimeoutException>() .Returns(Task.CompletedTask) .Throws <TimeoutException>() .Returns(Task.CompletedTask); IClient connectivityAwareClient = new ConnectivityAwareClient(client.Object, deviceConnectivityManager, deviceIdentity); ICloudProxy cloudProxy = new CloudProxy(connectivityAwareClient, Mock.Of <IMessageConverterProvider>(), "d1/m1", null, Mock.Of <ICloudListener>(), TimeSpan.FromHours(1), true); var connectionManager = Mock.Of <IConnectionManager>(c => c.GetCloudConnection("d1/m1") == Task.FromResult(Option.Some(cloudProxy))); deviceConnectivityManager.SetConnectionManager(connectionManager); int connected = 0; int disconnected = 0; deviceConnectivityManager.DeviceConnected += (_, __) => Interlocked.Increment(ref connected); deviceConnectivityManager.DeviceDisconnected += (_, __) => Interlocked.Increment(ref disconnected); // Assert await Task.Delay(TimeSpan.FromSeconds(15)); Assert.Equal(1, connected); Assert.Equal(0, disconnected); }
public async Task ConnectivityTestFailedTest() { // Arrange / act var deviceConnectivityManager = new DeviceConnectivityManager(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); var client = new Mock <IClient>(); client.SetupSequence(c => c.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>())) .Returns(Task.CompletedTask) .Throws <TimeoutException>() .Returns(Task.CompletedTask) .Throws <TimeoutException>() .Returns(Task.CompletedTask); IClient connectivityAwareClient = new ConnectivityAwareClient(client.Object, deviceConnectivityManager); var cloudProxy = new CloudProxy(connectivityAwareClient, Mock.Of <IMessageConverterProvider>(), "d1/m1", null); deviceConnectivityManager.SetTestCloudProxy(cloudProxy); int connected = 0; int disconnected = 0; deviceConnectivityManager.DeviceConnected += (_, __) => Interlocked.Increment(ref connected); deviceConnectivityManager.DeviceDisconnected += (_, __) => Interlocked.Increment(ref disconnected); // Assert await Task.Delay(TimeSpan.FromSeconds(15)); Assert.Equal(1, connected); Assert.Equal(0, disconnected); }
public async Task TestNoSubscriptionWhenOpenFails() { // Arrange int connectionStatusChangedHandlerCount = 0; void ConnectionStatusChangedHandler(ConnectionStatus status, ConnectionStatusChangeReason reason) { Interlocked.Increment(ref connectionStatusChangedHandlerCount); } var deviceConnectivityManager = new DeviceConnectivityManager(); var client = new Mock <IClient>(); client.Setup(c => c.OpenAsync()) .ThrowsAsync(new ArgumentException()); var connectivityAwareClient = new ConnectivityAwareClient(client.Object, deviceConnectivityManager, Mock.Of <IIdentity>(i => i.Id == "d1")); connectivityAwareClient.SetConnectionStatusChangedHandler(ConnectionStatusChangedHandler); // Act deviceConnectivityManager.InvokeDeviceConnected(); deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(0, connectionStatusChangedHandlerCount); // Act await Assert.ThrowsAsync <ArgumentException>(() => connectivityAwareClient.OpenAsync()); deviceConnectivityManager.InvokeDeviceConnected(); deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(0, connectionStatusChangedHandlerCount); }
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 <Core.IMessageConverterProvider>() .SingleInstance(); // IDeviceConnectivityManager builder.Register( c => { IDeviceConnectivityManager deviceConnectivityManager = new DeviceConnectivityManager(this.connectivityCheckFrequency, TimeSpan.FromMinutes(2)); 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(); // ICloudConnectionProvider builder.Register(c => new CloudConnectionProvider(c.Resolve <Core.IMessageConverterProvider>(), this.connectionPoolSize, c.Resolve <IClientProvider>(), this.upstreamProtocol)) .As <ICloudConnectionProvider>() .SingleInstance(); if (this.isStoreAndForwardEnabled || this.cacheTokens) { // Detect system environment builder.Register(c => new SystemEnvironment()) .As <ISystemEnvironment>() .SingleInstance(); // DataBase options builder.Register(c => new Storage.RocksDb.RocksDbOptionsProvider(c.Resolve <ISystemEnvironment>(), this.optimizeForPerformance)) .As <Storage.RocksDb.IRocksDbOptionsProvider>() .SingleInstance(); // IDbStore builder.Register( c => { var loggerFactory = c.Resolve <ILoggerFactory>(); ILogger logger = loggerFactory.CreateLogger(typeof(RoutingModule)); if (this.usePersistentStorage) { // Create partitions for messages and twins var partitionsList = new List <string> { Core.Constants.MessageStorePartitionKey, Core.Constants.TwinStorePartitionKey, Core.Constants.CheckpointStorePartitionKey }; try { IDbStoreProvider dbStoreprovider = Storage.RocksDb.DbStoreProvider.Create(c.Resolve <Storage.RocksDb.IRocksDbOptionsProvider>(), this.storagePath, partitionsList); logger.LogInformation($"Created persistent store at {this.storagePath}"); return(dbStoreprovider); } catch (Exception ex) when(!ExceptionEx.IsFatal(ex)) { logger.LogError(ex, "Error creating RocksDB store. Falling back to in-memory store."); return(new InMemoryDbStoreProvider()); } } else { logger.LogInformation($"Using in-memory store"); return(new InMemoryDbStoreProvider()); } }) .As <IDbStoreProvider>() .SingleInstance(); } // Task<ICredentialsStore> builder.Register(async c => { if (this.cacheTokens) { var dbStoreProvider = c.Resolve <IDbStoreProvider>(); IEncryptionProvider encryptionProvider = await this.workloadUri.Map( async uri => await EncryptionProvider.CreateAsync( this.storagePath, new Uri(uri), Service.Constants.WorkloadApiVersion, this.edgeModuleId, this.edgeModuleGenerationId.Expect(() => new InvalidOperationException("Missing generation ID")), Service.Constants.InitializationVectorFileName) as IEncryptionProvider) .GetOrElse(() => Task.FromResult <IEncryptionProvider>(NullEncryptionProvider.Instance)); IStoreProvider storeProvider = new StoreProvider(dbStoreProvider); IEntityStore <string, string> tokenCredentialsEntityStore = storeProvider.GetEntityStore <string, string>("tokenCredentials"); return(new TokenCredentialsStore(tokenCredentialsEntityStore, encryptionProvider)); } else { return(new NullCredentialsStore() as ICredentialsStore); } }) .As <Task <ICredentialsStore> >() .SingleInstance(); // IConnectionManager builder.Register(c => new ConnectionManager(c.Resolve <ICloudConnectionProvider>(), this.maxConnectedClients)) .As <IConnectionManager>() .SingleInstance(); // IEndpointFactory builder.Register(c => new EndpointFactory(c.Resolve <IConnectionManager>(), c.Resolve <Core.IMessageConverter <IRoutingMessage> >(), this.edgeDeviceId)) .As <IEndpointFactory>() .SingleInstance(); // RouteFactory builder.Register(c => new EdgeRouteFactory(c.Resolve <IEndpointFactory>())) .As <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(); // ITwinManager builder.Register(c => TwinManager.CreateTwinManager(c.Resolve <IConnectionManager>(), c.Resolve <IMessageConverterProvider>(), Option.None <IStoreProvider>())) .As <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(); // ICheckpointStore builder.Register(c => CheckpointStore.Create(c.Resolve <IDbStoreProvider>())) .As <ICheckpointStore>() .SingleInstance(); // IMessageStore builder.Register( c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var dbStoreProvider = c.Resolve <IDbStoreProvider>(); IStoreProvider storeProvider = new StoreProvider(dbStoreProvider); IMessageStore messageStore = new MessageStore(storeProvider, checkpointStore, TimeSpan.MaxValue); return(messageStore); }) .As <IMessageStore>() .SingleInstance(); // IEndpointExecutorFactory builder.Register( c => { var endpointExecutorConfig = c.Resolve <EndpointExecutorConfig>(); var messageStore = c.Resolve <IMessageStore>(); IEndpointExecutorFactory endpointExecutorFactory = new StoringAsyncEndpointExecutorFactory(endpointExecutorConfig, new AsyncEndpointExecutorOptions(10, TimeSpan.FromSeconds(10)), messageStore); return(endpointExecutorFactory); }) .As <IEndpointExecutorFactory>() .SingleInstance(); // Task<Router> builder.Register( async c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var routerConfig = c.Resolve <RouterConfig>(); var endpointExecutorFactory = c.Resolve <IEndpointExecutorFactory>(); return(await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory, checkpointStore)); }) .As <Task <Router> >() .SingleInstance(); // ITwinManager builder.Register(c => TwinManager.CreateTwinManager(c.Resolve <IConnectionManager>(), c.Resolve <IMessageConverterProvider>(), Option.Some <IStoreProvider>(new StoreProvider(c.Resolve <IDbStoreProvider>())))) .As <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<ICloudProxy> "EdgeHubCloudProxy" builder.Register( async c => { var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); var connectionManager = c.Resolve <IConnectionManager>(); Try <ICloudProxy> cloudProxyTry = await connectionManager.CreateCloudConnectionAsync(edgeHubCredentials); if (!cloudProxyTry.Success) { throw new EdgeHubConnectionException("Edge hub is unable to connect to IoT Hub", cloudProxyTry.Exception); } ICloudProxy cloudProxy = cloudProxyTry.Value; return(cloudProxy); }) .Named <Task <ICloudProxy> >("EdgeHubCloudProxy") .SingleInstance(); // Task<IEdgeHub> builder.Register( async c => { Router router = await c.Resolve <Task <Router> >(); IEdgeHub hub = new RoutingEdgeHub(router, c.Resolve <Core.IMessageConverter <IRoutingMessage> >(), c.Resolve <IConnectionManager>(), c.Resolve <ITwinManager>(), this.edgeDeviceId); return(hub); }) .As <Task <IEdgeHub> >() .SingleInstance(); // Task<ConfigUpdater> builder.Register( async c => { IMessageStore messageStore = this.isStoreAndForwardEnabled ? c.Resolve <IMessageStore>() : null; Router router = await c.Resolve <Task <Router> >(); var configUpdater = new ConfigUpdater(router, messageStore); return(configUpdater); }) .As <Task <ConfigUpdater> >() .SingleInstance(); // Task<IConfigSource> builder.Register( async c => { var routeFactory = c.Resolve <RouteFactory>(); if (this.useTwinConfig) { var connectionManager = c.Resolve <IConnectionManager>(); var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); var twinCollectionMessageConverter = c.Resolve <Core.IMessageConverter <TwinCollection> >(); var twinMessageConverter = c.Resolve <Core.IMessageConverter <Twin> >(); var twinManager = c.Resolve <ITwinManager>(); ICloudProxy cloudProxy = await c.ResolveNamed <Task <ICloudProxy> >("EdgeHubCloudProxy"); IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConfigSource edgeHubConnection = await EdgeHubConnection.Create( edgeHubCredentials.Identity as IModuleIdentity, edgeHub, twinManager, connectionManager, cloudProxy, routeFactory, twinCollectionMessageConverter, twinMessageConverter, this.versionInfo ); return(edgeHubConnection); } else { return(new LocalConfigSource(routeFactory, this.routes, this.storeAndForwardConfiguration)); } }) .As <Task <IConfigSource> >() .SingleInstance(); // Task<IConnectionProvider> builder.Register( async c => { IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConnectionProvider connectionProvider = new ConnectionProvider(c.Resolve <IConnectionManager>(), edgeHub); return(connectionProvider); }) .As <Task <IConnectionProvider> >() .SingleInstance(); base.Load(builder); }
public async Task WithDownstreamEventsTest() { // Arrange / act var deviceIdentity = Mock.Of <IIdentity>(i => i.Id == "d2"); var edgeHubIdentity = Mock.Of <IIdentity>(i => i.Id == "d1/m1"); var deviceConnectivityManager = new DeviceConnectivityManager(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), edgeHubIdentity); int connectedCallbackCount = 0; int disconnectedCallbackCount = 0; void ConnectionStatusChangedHandler(ConnectionStatus status, ConnectionStatusChangeReason reason) { if (status == ConnectionStatus.Connected && reason == ConnectionStatusChangeReason.Connection_Ok) { Interlocked.Increment(ref connectedCallbackCount); } else if (status == ConnectionStatus.Disconnected && reason == ConnectionStatusChangeReason.No_Network) { Interlocked.Increment(ref disconnectedCallbackCount); } } var device1UnderlyingClient = new Mock <IClient>(); device1UnderlyingClient.Setup(c => c.SendEventAsync(It.IsAny <Message>())) .Returns(Task.CompletedTask); var device1Client = new ConnectivityAwareClient(device1UnderlyingClient.Object, deviceConnectivityManager, deviceIdentity); device1Client.SetConnectionStatusChangedHandler(ConnectionStatusChangedHandler); var device2UnderlyingClient = new Mock <IClient>(); device2UnderlyingClient.Setup(c => c.SendEventAsync(It.IsAny <Message>())) .Throws <TimeoutException>(); var device2Client = new ConnectivityAwareClient(device2UnderlyingClient.Object, deviceConnectivityManager, deviceIdentity); device2Client.SetConnectionStatusChangedHandler(ConnectionStatusChangedHandler); var edgeHubUnderlyingClient = new Mock <IClient>(); edgeHubUnderlyingClient.Setup(c => c.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>())) .Throws <TimeoutException>(); IClient edgeHubClient = new ConnectivityAwareClient(edgeHubUnderlyingClient.Object, deviceConnectivityManager, deviceIdentity); edgeHubClient.SetConnectionStatusChangedHandler(ConnectionStatusChangedHandler); ICloudProxy cloudProxy = new CloudProxy(edgeHubClient, Mock.Of <IMessageConverterProvider>(), "d1/m1", null, Mock.Of <ICloudListener>(), TimeSpan.FromHours(1), true); var connectionManager = Mock.Of <IConnectionManager>(c => c.GetCloudConnection("d1/m1") == Task.FromResult(Option.Some(cloudProxy))); deviceConnectivityManager.SetConnectionManager(connectionManager); bool connected = false; deviceConnectivityManager.DeviceConnected += (_, __) => connected = true; deviceConnectivityManager.DeviceDisconnected += (_, __) => connected = false; // Act var cts = new CancellationTokenSource(); Task t = Task.Run( async() => { while (!cts.IsCancellationRequested) { await device1Client.SendEventAsync(new Message()); if (!cts.IsCancellationRequested) { await Task.Delay(TimeSpan.FromSeconds(1)); } } }); await Task.Delay(TimeSpan.FromSeconds(5)); // Assert Assert.True(connected); edgeHubUnderlyingClient.Verify(c => c.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>()), Times.Never); Assert.Equal(3, connectedCallbackCount); Assert.Equal(0, disconnectedCallbackCount); cts.Cancel(); await t; var cts2 = new CancellationTokenSource(); Task t2 = Task.Run( async() => { while (!cts2.IsCancellationRequested) { try { await device2Client.SendEventAsync(new Message()); } catch (TimeoutException) { } if (!cts2.IsCancellationRequested) { await Task.Delay(TimeSpan.FromSeconds(1)); } } }); await Task.Delay(TimeSpan.FromSeconds(5)); // Assert Assert.False(connected); edgeHubUnderlyingClient.Verify(c => c.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>()), Times.Once); Assert.Equal(3, connectedCallbackCount); Assert.Equal(3, disconnectedCallbackCount); cts2.Cancel(); await t2; await device1Client.SendEventAsync(new Message()); // Assert Assert.True(connected); edgeHubUnderlyingClient.Verify(c => c.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>()), Times.Once); Assert.Equal(6, connectedCallbackCount); Assert.Equal(3, disconnectedCallbackCount); }
public async Task ConnectivityChangeTest() { // Arrange var receivedConnectionStatuses = new List <ConnectionStatus>(); var receivedChangeReasons = new List <ConnectionStatusChangeReason>(); void ConnectionStatusChangedHandler(ConnectionStatus status, ConnectionStatusChangeReason reason) { receivedConnectionStatuses.Add(status); receivedChangeReasons.Add(reason); } var deviceConnectivityManager = new DeviceConnectivityManager(); var client = Mock.Of <IClient>(); Mock.Get(client).SetupSequence(c => c.SendEventAsync(It.IsAny <Client.Message>())) .Returns(Task.CompletedTask) .Throws(new TimeoutException()); var connectivityAwareClient = new ConnectivityAwareClient(client, deviceConnectivityManager, Mock.Of <IIdentity>(i => i.Id == "d1")); connectivityAwareClient.SetConnectionStatusChangedHandler(ConnectionStatusChangedHandler); // Act await connectivityAwareClient.SendEventAsync(new Client.Message()); // Assert Assert.Equal(1, receivedConnectionStatuses.Count); Assert.Equal(ConnectionStatus.Connected, receivedConnectionStatuses[0]); Assert.Equal(ConnectionStatusChangeReason.Connection_Ok, receivedChangeReasons[0]); // Act await Assert.ThrowsAsync <TimeoutException>(async() => await connectivityAwareClient.SendEventAsync(new Client.Message())); // Assert Assert.Equal(1, receivedConnectionStatuses.Count); Assert.Equal(ConnectionStatus.Connected, receivedConnectionStatuses[0]); Assert.Equal(ConnectionStatusChangeReason.Connection_Ok, receivedChangeReasons[0]); // Act deviceConnectivityManager.InvokeDeviceConnected(); // Assert Assert.Equal(1, receivedConnectionStatuses.Count); Assert.Equal(ConnectionStatus.Connected, receivedConnectionStatuses[0]); Assert.Equal(ConnectionStatusChangeReason.Connection_Ok, receivedChangeReasons[0]); // Act deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(2, receivedConnectionStatuses.Count); Assert.Equal(ConnectionStatus.Disconnected, receivedConnectionStatuses[1]); Assert.Equal(ConnectionStatusChangeReason.No_Network, receivedChangeReasons[1]); // Act deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(2, receivedConnectionStatuses.Count); Assert.Equal(ConnectionStatus.Disconnected, receivedConnectionStatuses[1]); Assert.Equal(ConnectionStatusChangeReason.No_Network, receivedChangeReasons[1]); // Act await connectivityAwareClient.CloseAsync(); deviceConnectivityManager.InvokeDeviceConnected(); deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(2, receivedConnectionStatuses.Count); Assert.Equal(ConnectionStatus.Disconnected, receivedConnectionStatuses[1]); Assert.Equal(ConnectionStatusChangeReason.No_Network, receivedChangeReasons[1]); }
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 => { IDeviceConnectivityManager deviceConnectivityManager = new DeviceConnectivityManager(this.connectivityCheckFrequency, TimeSpan.FromMinutes(2)); 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 messageConverterProvider = c.Resolve <IMessageConverterProvider>(); var clientProvider = c.Resolve <IClientProvider>(); var tokenProvider = c.ResolveNamed <ITokenProvider>("EdgeHubClientAuthTokenProvider"); IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await c.Resolve <Task <IDeviceScopeIdentitiesCache> >(); ICloudConnectionProvider cloudConnectionProvider = new CloudConnectionProvider( messageConverterProvider, this.connectionPoolSize, clientProvider, this.upstreamProtocol, tokenProvider, deviceScopeIdentitiesCache, TimeSpan.FromMinutes(60)); return(cloudConnectionProvider); }) .As <Task <ICloudConnectionProvider> >() .SingleInstance(); // Task<ICredentialsStore> builder.Register(async c => { if (this.cacheTokens) { IKeyValueStore <string, string> encryptedStore = await c.ResolveNamed <Task <IKeyValueStore <string, string> > >("EncryptedStore"); return(new TokenCredentialsStore(encryptedStore)); } else { return(new NullCredentialsStore() as ICredentialsStore); } }) .As <Task <ICredentialsStore> >() .SingleInstance(); // Task<IConnectionManager> builder.Register( async c => { ICloudConnectionProvider cloudConnectionProvider = await c.Resolve <Task <ICloudConnectionProvider> >(); IConnectionManager connectionManager = new ConnectionManager(cloudConnectionProvider, this.maxConnectedClients); 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) 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 => { 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(); // ICheckpointStore builder.Register(c => CheckpointStore.Create(c.Resolve <IDbStoreProvider>())) .As <ICheckpointStore>() .SingleInstance(); // IMessageStore builder.Register( c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var dbStoreProvider = c.Resolve <IDbStoreProvider>(); IStoreProvider storeProvider = new StoreProvider(dbStoreProvider); IMessageStore messageStore = new MessageStore(storeProvider, checkpointStore, TimeSpan.MaxValue); return(messageStore); }) .As <IMessageStore>() .SingleInstance(); // IEndpointExecutorFactory builder.Register( c => { var endpointExecutorConfig = c.Resolve <EndpointExecutorConfig>(); var messageStore = c.Resolve <IMessageStore>(); IEndpointExecutorFactory endpointExecutorFactory = new StoringAsyncEndpointExecutorFactory(endpointExecutorConfig, new AsyncEndpointExecutorOptions(10, TimeSpan.FromSeconds(10)), messageStore); return(endpointExecutorFactory); }) .As <IEndpointExecutorFactory>() .SingleInstance(); // Task<Router> builder.Register( async c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var routerConfig = c.Resolve <RouterConfig>(); var endpointExecutorFactory = c.Resolve <IEndpointExecutorFactory>(); return(await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory, checkpointStore)); }) .As <Task <Router> >() .SingleInstance(); // Task<ITwinManager> builder.Register(async c => { var dbStoreProvider = c.Resolve <IDbStoreProvider>(); var messageConverterProvider = c.Resolve <IMessageConverterProvider>(); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); return(TwinManager.CreateTwinManager(connectionManager, messageConverterProvider, Option.Some <IStoreProvider>(new StoreProvider(dbStoreProvider)))); }) .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<ICloudProxy> "EdgeHubCloudProxy" builder.Register( async c => { var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); Try <ICloudProxy> cloudProxyTry = await connectionManager.CreateCloudConnectionAsync(edgeHubCredentials); if (!cloudProxyTry.Success) { throw new EdgeHubConnectionException("Edge hub is unable to connect to IoT Hub", cloudProxyTry.Exception); } ICloudProxy cloudProxy = cloudProxyTry.Value; return(cloudProxy); }) .Named <Task <ICloudProxy> >("EdgeHubCloudProxy") .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<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> >(); Router router = await routerTask; ITwinManager twinManager = await twinManagerTask; IConnectionManager connectionManager = await connectionManagerTask; IInvokeMethodHandler invokeMethodHandler = await invokeMethodHandlerTask; IEdgeHub hub = new RoutingEdgeHub(router, routingMessageConverter, connectionManager, twinManager, this.edgeDeviceId, invokeMethodHandler); return(hub); }) .As <Task <IEdgeHub> >() .SingleInstance(); // Task<ConfigUpdater> builder.Register( async c => { IMessageStore messageStore = this.isStoreAndForwardEnabled ? c.Resolve <IMessageStore>() : null; Router router = await c.Resolve <Task <Router> >(); var configUpdater = new ConfigUpdater(router, messageStore); return(configUpdater); }) .As <Task <ConfigUpdater> >() .SingleInstance(); // Task<IConfigSource> builder.Register( 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> >(); ITwinManager twinManager = await c.Resolve <Task <ITwinManager> >(); ICloudProxy cloudProxy = await c.ResolveNamed <Task <ICloudProxy> >("EdgeHubCloudProxy"); IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await c.Resolve <Task <IDeviceScopeIdentitiesCache> >(); IConfigSource edgeHubConnection = await EdgeHubConnection.Create( edgeHubCredentials, edgeHub, twinManager, connectionManager, cloudProxy, routeFactory, twinCollectionMessageConverter, twinMessageConverter, this.versionInfo, deviceScopeIdentitiesCache ); return(edgeHubConnection); } else { return(new LocalConfigSource(routeFactory, this.routes, this.storeAndForwardConfiguration)); } }) .As <Task <IConfigSource> >() .SingleInstance(); // Task<IConnectionProvider> builder.Register( async c => { IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConnectionProvider connectionProvider = new ConnectionProvider(connectionManager, edgeHub); return(connectionProvider); }) .As <Task <IConnectionProvider> >() .SingleInstance(); base.Load(builder); }
public async Task ConnectivityChangeEventTest2() { // Arrange int connectedStatusChangedHandlerCount = 0; int disconnectedStatusChangedHandlerCount = 0; void ConnectionStatusChangedHandler(ConnectionStatus status, ConnectionStatusChangeReason reason) { if (status == ConnectionStatus.Connected) { Interlocked.Increment(ref connectedStatusChangedHandlerCount); } else { Interlocked.Increment(ref disconnectedStatusChangedHandlerCount); } } var deviceConnectivityManager = new DeviceConnectivityManager(); ConnectionStatusChangesHandler innerClientHandler = null; var client = new Mock <IClient>(); client.Setup(c => c.SetConnectionStatusChangedHandler(It.IsAny <ConnectionStatusChangesHandler>())) .Callback <ConnectionStatusChangesHandler>(c => innerClientHandler = c); var connectivityAwareClient = new ConnectivityAwareClient(client.Object, deviceConnectivityManager, Mock.Of <IIdentity>(i => i.Id == "d1")); connectivityAwareClient.SetConnectionStatusChangedHandler(ConnectionStatusChangedHandler); // Act await connectivityAwareClient.OpenAsync(); // Assert Assert.NotNull(innerClientHandler); // Act deviceConnectivityManager.InvokeDeviceConnected(); // Assert Assert.Equal(1, connectedStatusChangedHandlerCount); Assert.Equal(0, disconnectedStatusChangedHandlerCount); // Act innerClientHandler.Invoke(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); // Assert Assert.Equal(1, connectedStatusChangedHandlerCount); Assert.Equal(0, disconnectedStatusChangedHandlerCount); // Act deviceConnectivityManager.InvokeDeviceDisconnected(); // Assert Assert.Equal(1, connectedStatusChangedHandlerCount); Assert.Equal(1, disconnectedStatusChangedHandlerCount); // Act innerClientHandler.Invoke(ConnectionStatus.Disconnected, ConnectionStatusChangeReason.Communication_Error); // Assert Assert.Equal(1, connectedStatusChangedHandlerCount); Assert.Equal(1, disconnectedStatusChangedHandlerCount); }