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 _));
        }
Exemple #3
0
        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);
        }
Exemple #5
0
        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);
            });
        }
Exemple #6
0
        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 _));
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        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 _));
        }
Exemple #9
0
        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}");
        }
Exemple #10
0
        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();
        }
Exemple #11
0
        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());
        }