public async Task DisconnectedCircuitIsRemovedAfterConfiguredTimeout() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var circuitOptions = new CircuitOptions { DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(3), }; var registry = new TestCircuitRegistry(circuitIdFactory, circuitOptions); var tcs = new TaskCompletionSource <object>(); registry.OnAfterEntryEvicted = () => { tcs.TrySetResult(new object()); }; var circuitHost = TestCircuitHost.Create(); registry.RegisterDisconnectedCircuit(circuitHost); // Act // Verify it's present in the dictionary. Assert.True(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out var _)); await Task.Run(() => tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10))); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out var _)); }
public async Task ConnectAsync_InvokesCircuitHandlers_WhenCircuitWasPreviouslyDisconnected() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = CreateRegistry(circuitIdFactory); var handler = new Mock <CircuitHandler> { CallBase = true }; var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId(), handlers: new[] { handler.Object }); registry.DisconnectedCircuits.Set(circuitHost.CircuitId, circuitHost, new MemoryCacheEntryOptions { Size = 1 }); var newClient = Mock.Of <IClientProxy>(); var newConnectionId = "new-id"; // Act var result = await registry.ConnectAsync(circuitHost.CircuitId, newClient, newConnectionId, default); // Assert Assert.NotNull(result); handler.Verify(v => v.OnCircuitOpenedAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Never()); handler.Verify(v => v.OnConnectionUpAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Once()); handler.Verify(v => v.OnConnectionDownAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Never()); handler.Verify(v => v.OnCircuitClosedAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Never()); }
public async Task ConnectAsync_MakesInactiveCircuitActive() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = CreateRegistry(circuitIdFactory); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.DisconnectedCircuits.Set(circuitHost.CircuitId, circuitHost, new MemoryCacheEntryOptions { Size = 1 }); var newClient = Mock.Of <IClientProxy>(); var newConnectionId = "new-id"; // Act var result = await registry.ConnectAsync(circuitHost.CircuitId, newClient, newConnectionId, default); // Assert Assert.Same(circuitHost, result); Assert.Same(newClient, circuitHost.Client.Client); Assert.Same(newConnectionId, circuitHost.Client.ConnectionId); var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId, out _)); }
public void CircuitRegistryUsesConfiguredMaxRetainedDisconnectedCircuitsValue() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var maxCircuits = 3; var circuitOptions = new CircuitOptions { MaxRetainedDisconnectedCircuits = maxCircuits, }; var registry = new TestCircuitRegistry(circuitIdFactory, circuitOptions); var hosts = Enumerable.Range(0, maxCircuits + 2) .Select(_ => TestCircuitHost.Create()) .ToArray(); // Act for (var i = 0; i < hosts.Length; i++) { registry.RegisterDisconnectedCircuit(hosts[i]); } // Assert for (var i = 0; i < maxCircuits; i++) { Assert.True(registry.DisconnectedCircuits.TryGetValue(hosts[i].CircuitId, out var _)); } // Additional circuits do not get registered. Assert.False(registry.DisconnectedCircuits.TryGetValue(hosts[maxCircuits].CircuitId, out var _)); Assert.False(registry.DisconnectedCircuits.TryGetValue(hosts[maxCircuits + 1].CircuitId, out var _)); }
public async Task ConnectAsync_TransfersClientOnActiveCircuit() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = CreateRegistry(circuitIdFactory); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.Register(circuitHost); var newClient = Mock.Of <IClientProxy>(); var newConnectionId = "new-id"; // Act var result = await registry.ConnectAsync(circuitHost.CircuitId, newClient, newConnectionId, default); // Assert Assert.Same(circuitHost, result); Assert.Same(newClient, circuitHost.Client.Client); Assert.Same(newConnectionId, circuitHost.Client.ConnectionId); var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); }
private static CircuitRegistry CreateRegistry(CircuitIdFactory factory = null) { return(new CircuitRegistry( Options.Create(new CircuitOptions()), NullLogger <CircuitRegistry> .Instance, factory ?? TestCircuitIdFactory.CreateTestFactory())); }
public async Task ReconnectBeforeTimeoutDoesNotGetEntryToBeEvicted() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var circuitOptions = new CircuitOptions { DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(8), }; var registry = new TestCircuitRegistry(circuitIdFactory, circuitOptions); var tcs = new TaskCompletionSource <object>(); registry.OnAfterEntryEvicted = () => { tcs.TrySetResult(new object()); }; var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.RegisterDisconnectedCircuit(circuitHost); await registry.ConnectAsync(circuitHost.CircuitId, Mock.Of <IClientProxy>(), "new-connection", default); // Act await Task.Run(() => tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10))); // Verify it's still connected Assert.True(registry.ConnectedCircuits.TryGetValue(circuitHost.CircuitId, out var cacheValue)); Assert.Same(circuitHost, cacheValue); // Nothing should be disconnected. Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId.Secret, out var _)); }
public async Task DisconnectWhenAConnectIsInProgress() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = new TestCircuitRegistry(circuitIdFactory); registry.BeforeConnect = new ManualResetEventSlim(); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.Register(circuitHost); var client = Mock.Of <IClientProxy>(); var oldId = circuitHost.Client.ConnectionId; var newId = "new-connection"; // Act var connect = Task.Run(() => registry.ConnectAsync(circuitHost.CircuitId, client, newId, default)); var disconnect = Task.Run(() => registry.DisconnectAsync(circuitHost, oldId)); registry.BeforeConnect.Set(); await Task.WhenAll(connect, disconnect); // Assert // We expect the disconnect to fail since the client identifier has changed. var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); Assert.Same(client, circuitHost.Client.Client); Assert.Equal(newId, circuitHost.Client.ConnectionId); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId, out _)); }
public async Task ConnectAsync_InvokesCircuitHandlers_DisposesCircuitOnFailure() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = CreateRegistry(circuitIdFactory); var handler = new Mock <CircuitHandler> { CallBase = true }; handler.Setup(h => h.OnConnectionUpAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>())).Throws(new InvalidTimeZoneException()); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId(), handlers: new[] { handler.Object }); registry.Register(circuitHost); var newClient = Mock.Of <IClientProxy>(); var newConnectionId = "new-id"; // Act var result = await registry.ConnectAsync(circuitHost.CircuitId, newClient, newConnectionId, default); // Assert Assert.Null(result); Assert.Null(circuitHost.Handle.CircuitHost); // Will be null if disposed. Assert.Empty(registry.ConnectedCircuits); Assert.Equal(0, registry.DisconnectedCircuits.Count); }
public void ValidateCircuitId_ReturnsFalseForMalformedPayloads() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); // Act var isValid = factory.TryParseCircuitId("$%@&==", out _); // Assert Assert.False(isValid, "Accepted an invalid payload"); }
public void CircuitIds_Roundtrip() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); var id = factory.CreateCircuitId(); // Act var isValid = factory.ValidateCircuitId(id); // Assert Assert.True(isValid, "Failed to validate id"); }
public void CreateCircuitId_Generates_GeneratesDifferentIds_ForSuccessiveCalls() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); // Act var secrets = Enumerable.Range(0, 100).Select(i => factory.CreateCircuitId()).Select(s => s.Secret).ToArray(); // Assert Assert.All(secrets, secret => Assert.NotNull(secret)); Assert.Equal(100, secrets.Distinct(StringComparer.Ordinal).Count()); }
public void CreateCircuitId_Generates_NewRandomId() { var factory = TestCircuitIdFactory.CreateTestFactory(); // Act var secret = factory.CreateCircuitId(); // Assert Assert.NotNull(secret.Secret); // This is the magic data protection header that validates its protected Assert.StartsWith("CfDJ", secret.Secret); }
public void CircuitIds_Roundtrip() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); var id = factory.CreateCircuitId(); // Act var isValid = factory.TryParseCircuitId(id.Secret, out var parsed); // Assert Assert.True(isValid, "Failed to validate id"); Assert.Equal(id, parsed); Assert.Equal(id.Secret, parsed.Secret); Assert.Equal(id.Id, parsed.Id); }
public async Task Connect_WhileDisconnectIsInProgress() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = new TestCircuitRegistry(circuitIdFactory); registry.BeforeDisconnect = new ManualResetEventSlim(); var tcs = new TaskCompletionSource <int>(); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId()); registry.Register(circuitHost); var client = Mock.Of <IClientProxy>(); var newId = "new-connection"; // Act var disconnect = Task.Run(() => { var task = registry.DisconnectAsync(circuitHost, circuitHost.Client.ConnectionId); tcs.SetResult(0); return(task); }); var connect = Task.Run(async() => { registry.BeforeDisconnect.Set(); await tcs.Task; await registry.ConnectAsync(circuitHost.CircuitId, client, newId, default); }); registry.BeforeDisconnect.Set(); await Task.WhenAll(disconnect, connect); // Assert // We expect the disconnect to finish followed by a reconnect var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); Assert.Same(client, circuitHost.Client.Client); Assert.Equal(newId, circuitHost.Client.ConnectionId); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId, out _)); }
public void ValidateCircuitId_ReturnsFalseForPotentiallyTamperedPayloads() { // Arrange var factory = TestCircuitIdFactory.CreateTestFactory(); var secret = factory.CreateCircuitId(); var protectedBytes = Base64UrlTextEncoder.Decode(secret.Secret); for (int i = protectedBytes.Length - 10; i < protectedBytes.Length; i++) { protectedBytes[i] = 0; } var tampered = Base64UrlTextEncoder.Encode(protectedBytes); // Act var isValid = factory.TryParseCircuitId(tampered, out _); // Assert Assert.False(isValid, "Accepted a tampered payload"); }
public async Task Connect_WhileDisconnectIsInProgress_SeriallyExecutesCircuitHandlers() { // Arrange var circuitIdFactory = TestCircuitIdFactory.CreateTestFactory(); var registry = new TestCircuitRegistry(circuitIdFactory); registry.BeforeDisconnect = new ManualResetEventSlim(); // This verifies that connection up \ down events on a circuit handler are always invoked serially. var circuitHandler = new SerialCircuitHandler(); var tcs = new TaskCompletionSource <int>(); var circuitHost = TestCircuitHost.Create(circuitIdFactory.CreateCircuitId(), handlers: new[] { circuitHandler }); registry.Register(circuitHost); var client = Mock.Of <IClientProxy>(); var newId = "new-connection"; // Act var disconnect = Task.Run(() => { var task = registry.DisconnectAsync(circuitHost, circuitHost.Client.ConnectionId); tcs.SetResult(0); return(task); }); var connect = Task.Run(async() => { registry.BeforeDisconnect.Set(); await tcs.Task; await registry.ConnectAsync(circuitHost.CircuitId, client, newId, default); }); await Task.WhenAll(disconnect, connect); // Assert Assert.Single(registry.ConnectedCircuits.Values); Assert.False(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId, out _)); Assert.True(circuitHandler.OnConnectionDownExecuted, "OnConnectionDownAsync should have been executed."); Assert.True(circuitHandler.OnConnectionUpExecuted, "OnConnectionUpAsync should have been executed."); }