// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Browser; using Microsoft.AspNetCore.Components.Browser.Rendering; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; using Moq; using Xunit; namespace Microsoft.AspNetCore.Components.Server.Circuits { public class CircuitHostTest { [Fact] public async Task DisposeAsync_DisposesResources() { // Arrange var serviceScope = new Mock(); var remoteRenderer = GetRemoteRenderer(); var circuitHost = GetCircuitHost( serviceScope.Object, remoteRenderer); // Act await circuitHost.DisposeAsync(); // Assert serviceScope.Verify(s => s.Dispose(), Times.Once()); Assert.True(remoteRenderer.Disposed); } [Fact] public async Task InitializeAsync_InvokesHandlers() { // Arrange var cancellationToken = new CancellationToken(); var handler1 = new Mock(MockBehavior.Strict); var handler2 = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); handler1 .InSequence(sequence) .Setup(h => h.OnCircuitOpenedAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler2 .InSequence(sequence) .Setup(h => h.OnCircuitOpenedAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler1 .InSequence(sequence) .Setup(h => h.OnConnectionUpAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler2 .InSequence(sequence) .Setup(h => h.OnConnectionUpAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); var circuitHost = GetCircuitHost(handlers: new[] { handler1.Object, handler2.Object }); // Act await circuitHost.InitializeAsync(cancellationToken); // Assert handler1.VerifyAll(); handler2.VerifyAll(); } [Fact] public async Task DisposeAsync_InvokesCircuitHandler() { // Arrange var cancellationToken = new CancellationToken(); var handler1 = new Mock(MockBehavior.Strict); var handler2 = new Mock(MockBehavior.Strict); var sequence = new MockSequence(); handler1 .InSequence(sequence) .Setup(h => h.OnConnectionDownAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler2 .InSequence(sequence) .Setup(h => h.OnConnectionDownAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler1 .InSequence(sequence) .Setup(h => h.OnCircuitClosedAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); handler2 .InSequence(sequence) .Setup(h => h.OnCircuitClosedAsync(It.IsAny(), cancellationToken)) .Returns(Task.CompletedTask) .Verifiable(); var circuitHost = GetCircuitHost(handlers: new[] { handler1.Object, handler2.Object }); // Act await circuitHost.DisposeAsync(); // Assert handler1.VerifyAll(); handler2.VerifyAll(); } private static CircuitHost GetCircuitHost( IServiceScope serviceScope = null, RemoteRenderer remoteRenderer = null, CircuitHandler[] handlers = null) { serviceScope = serviceScope ?? Mock.Of(); var clientProxy = Mock.Of(); var renderRegistry = new RendererRegistry(); var jsRuntime = Mock.Of(); var syncContext = new CircuitSynchronizationContext(); remoteRenderer = remoteRenderer ?? GetRemoteRenderer(); handlers = handlers ?? Array.Empty(); return new CircuitHost( serviceScope, clientProxy, renderRegistry, remoteRenderer, configure: _ => { }, jsRuntime: jsRuntime, synchronizationContext: syncContext, handlers); } private static TestRemoteRenderer GetRemoteRenderer() { return new TestRemoteRenderer( Mock.Of(), new RendererRegistry(), Mock.Of(), Mock.Of(), new CircuitSynchronizationContext()); } private class TestRemoteRenderer : RemoteRenderer { public TestRemoteRenderer(IServiceProvider serviceProvider, RendererRegistry rendererRegistry, IJSRuntime jsRuntime, IClientProxy client, SynchronizationContext syncContext) : base(serviceProvider, rendererRegistry, jsRuntime, client, syncContext) { } public bool Disposed { get; set; } protected override void Dispose(bool disposing) { base.Dispose(disposing); Disposed = true; } } } }