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 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 DisposeAsync_DisposesScopeAsynchronouslyIfPossible() { // Arrange var serviceScope = new Mock <IServiceScope>(); serviceScope .As <IAsyncDisposable>() .Setup(f => f.DisposeAsync()) .Returns(new ValueTask(Task.CompletedTask)) .Verifiable(); var remoteRenderer = GetRemoteRenderer(); var circuitHost = TestCircuitHost.Create( serviceScope: serviceScope.Object, remoteRenderer: remoteRenderer); // Act await circuitHost.DisposeAsync(); // Assert serviceScope.Verify(s => s.Dispose(), Times.Never()); serviceScope.As <IAsyncDisposable>().Verify(s => s.DisposeAsync(), Times.Once()); Assert.True(remoteRenderer.Disposed); Assert.Null(circuitHost.Handle.CircuitHost); }
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 async Task DisposeAsync_MarksJSRuntimeAsDisconnectedBeforeDisposingRenderer() { // Arrange var serviceScope = new Mock <IServiceScope>(); var remoteRenderer = GetRemoteRenderer(); var circuitHost = TestCircuitHost.Create( serviceScope: new AsyncServiceScope(serviceScope.Object), remoteRenderer: remoteRenderer); var component = new PerformJSInteropOnDisposeComponent(circuitHost.JSRuntime); circuitHost.Renderer.AssignRootComponentId(component); var circuitUnhandledExceptions = new List <UnhandledExceptionEventArgs>(); circuitHost.UnhandledException += (sender, eventArgs) => { circuitUnhandledExceptions.Add(eventArgs); }; // Act await circuitHost.DisposeAsync(); // Assert: Component disposal logic sees the exception var componentException = Assert.IsType <JSDisconnectedException>(component.ExceptionDuringDisposeAsync); // Assert: Circuit host notifies about the exception Assert.Collection(circuitUnhandledExceptions, eventArgs => { Assert.Same(componentException, eventArgs.ExceptionObject); }); }
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 void Register_AddsCircuit() { // Arrange var registry = CreateRegistry(); var circuitHost = TestCircuitHost.Create(); // Act registry.Register(circuitHost); // Assert var actual = Assert.Single(registry.ConnectedCircuits.Values); Assert.Same(circuitHost, actual); }
public async Task DisconnectAsync_DoesNothing_IfCircuitIsInactive() { // Arrange var registry = CreateRegistry(); var circuitHost = TestCircuitHost.Create(); registry.DisconnectedCircuits.Set(circuitHost.CircuitId, circuitHost, new MemoryCacheEntryOptions { Size = 1 }); // Act await registry.DisconnectAsync(circuitHost, circuitHost.Client.ConnectionId); // Assert Assert.Empty(registry.ConnectedCircuits.Values); Assert.True(registry.DisconnectedCircuits.TryGetValue(circuitHost.CircuitId, out _)); }
public async Task InitializeAsync_RendersRootComponentsInParallel() { // To test that root components are run in parallel, we ensure that each root component // finishes rendering (i.e. returns from SetParametersAsync()) only after all other // root components have started rendering. If the root components were rendered // sequentially, the 1st component would get stuck rendering forever because the // 2nd component had not yet started rendering. We call RenderInParallelComponent.Setup() // to configure how many components will be rendered in advance so that each component // can be assigned a TaskCompletionSource and await the same array of tasks. A timeout // is configured for circuitHost.InitializeAsync() so that the test can fail rather than // hang forever. // Arrange var componentCount = 3; var initializeTimeout = TimeSpan.FromMilliseconds(5000); var cancellationToken = new CancellationToken(); var serviceScope = new Mock <IServiceScope>(); var descriptors = new List <ComponentDescriptor>(); RenderInParallelComponent.Setup(componentCount); for (var i = 0; i < componentCount; i++) { descriptors.Add(new() { ComponentType = typeof(RenderInParallelComponent), Parameters = ParameterView.Empty, Sequence = 0 }); } var circuitHost = TestCircuitHost.Create( serviceScope: new AsyncServiceScope(serviceScope.Object), descriptors: descriptors); // Act object initializeException = null; circuitHost.UnhandledException += (sender, eventArgs) => initializeException = eventArgs.ExceptionObject; var initializeTask = circuitHost.InitializeAsync(new ProtectedPrerenderComponentApplicationStore(Mock.Of <IDataProtectionProvider>()), cancellationToken); await initializeTask.WaitAsync(initializeTimeout); // Assert: This was not reached only because an exception was thrown in InitializeAsync() Assert.True(initializeException is null, $"An exception was thrown in {nameof(TestCircuitHost.InitializeAsync)}(): {initializeException}"); }
public async Task InitializeAsync_InvokesHandlers() { // Arrange var cancellationToken = new CancellationToken(); var handler1 = new Mock <CircuitHandler>(MockBehavior.Strict); var handler2 = new Mock <CircuitHandler>(MockBehavior.Strict); var sequence = new MockSequence(); handler1 .InSequence(sequence) .Setup(h => h.OnCircuitOpenedAsync(It.IsAny <Circuit>(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler2 .InSequence(sequence) .Setup(h => h.OnCircuitOpenedAsync(It.IsAny <Circuit>(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler1 .InSequence(sequence) .Setup(h => h.OnConnectionUpAsync(It.IsAny <Circuit>(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler2 .InSequence(sequence) .Setup(h => h.OnConnectionUpAsync(It.IsAny <Circuit>(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); var circuitHost = TestCircuitHost.Create(handlers: new[] { handler1.Object, handler2.Object }); // Act await circuitHost.InitializeAsync(new ProtectedPrerenderComponentApplicationStore(Mock.Of <IDataProtectionProvider>()), cancellationToken); // Assert handler1.VerifyAll(); handler2.VerifyAll(); }
public async Task ConnectAsync_InvokesCircuitHandlers_WhenCircuitWasConsideredConnected() { // Arrange var registry = CreateRegistry(); var handler = new Mock <CircuitHandler> { CallBase = true }; var circuitHost = TestCircuitHost.Create(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.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.Once()); handler.Verify(v => v.OnCircuitClosedAsync(It.IsAny <Circuit>(), It.IsAny <CancellationToken>()), Times.Never()); }