// 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.Buffers; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.SignalR.Protocol; using Moq; using Xunit; namespace Microsoft.AspNetCore.SignalR.Client.Tests { public partial class HubConnectionTests { [Fact] public async Task InvokeThrowsIfSerializingMessageFails() { var exception = new InvalidOperationException(); var hubConnection = CreateHubConnection(new TestConnection(), protocol: MockHubProtocol.Throw(exception)); await hubConnection.StartAsync().OrTimeout(); var actualException = await Assert.ThrowsAsync(async () => await hubConnection.InvokeAsync("test").OrTimeout()); Assert.Same(exception, actualException); } [Fact] public async Task SendAsyncThrowsIfSerializingMessageFails() { var exception = new InvalidOperationException(); var hubConnection = CreateHubConnection(new TestConnection(), protocol: MockHubProtocol.Throw(exception)); await hubConnection.StartAsync().OrTimeout(); var actualException = await Assert.ThrowsAsync(async () => await hubConnection.SendAsync("test").OrTimeout()); Assert.Same(exception, actualException); } [Fact] public async Task ClosedEventRaisedWhenTheClientIsStopped() { var builder = new HubConnectionBuilder(); builder.WithConnectionFactory(format => new TestConnection().StartAsync(format)); var hubConnection = builder.Build(); var closedEventTcs = new TaskCompletionSource(); hubConnection.Closed += e => closedEventTcs.SetResult(e); await hubConnection.StartAsync().OrTimeout(); await hubConnection.StopAsync().OrTimeout(); Assert.Null(await closedEventTcs.Task); } [Fact] public async Task PendingInvocationsAreCancelledWhenConnectionClosesCleanly() { var hubConnection = CreateHubConnection(new TestConnection()); await hubConnection.StartAsync().OrTimeout(); var invokeTask = hubConnection.InvokeAsync("testMethod").OrTimeout(); await hubConnection.StopAsync().OrTimeout(); await Assert.ThrowsAsync(async () => await invokeTask); } [Fact] public async Task PendingInvocationsAreTerminatedWithExceptionWhenTransportCompletesWithError() { var connection = new TestConnection(); var hubConnection = CreateHubConnection(connection, protocol: Mock.Of()); await hubConnection.StartAsync().OrTimeout(); var invokeTask = hubConnection.InvokeAsync("testMethod").OrTimeout(); var exception = new InvalidOperationException(); connection.CompleteFromTransport(exception); var actualException = await Assert.ThrowsAsync(async () => await invokeTask); Assert.Equal(exception, actualException); } [Fact] public async Task ConnectionTerminatedIfServerTimeoutIntervalElapsesWithNoMessages() { var hubConnection = CreateHubConnection(new TestConnection()); hubConnection.ServerTimeout = TimeSpan.FromMilliseconds(100); var closeTcs = new TaskCompletionSource(); hubConnection.Closed += ex => closeTcs.TrySetResult(ex); await hubConnection.StartAsync().OrTimeout(); var exception = Assert.IsType(await closeTcs.Task.OrTimeout()); Assert.Equal("Server timeout (100.00ms) elapsed without receiving a message from the server.", exception.Message); } [Fact] public async Task PendingInvocationsAreTerminatedIfServerTimeoutIntervalElapsesWithNoMessages() { var hubConnection = CreateHubConnection(new TestConnection()); hubConnection.ServerTimeout = TimeSpan.FromMilliseconds(500); await hubConnection.StartAsync().OrTimeout(); // Start an invocation (but we won't complete it) var invokeTask = hubConnection.InvokeAsync("Method").OrTimeout(); var exception = await Assert.ThrowsAsync(() => invokeTask); Assert.Equal("Server timeout (500.00ms) elapsed without receiving a message from the server.", exception.Message); } // Moq really doesn't handle out parameters well, so to make these tests work I added a manual mock -anurse private class MockHubProtocol : IHubProtocol { private HubInvocationMessage _parsed; private Exception _error; public static MockHubProtocol ReturnOnParse(HubInvocationMessage parsed) { return new MockHubProtocol { _parsed = parsed }; } public static MockHubProtocol Throw(Exception error) { return new MockHubProtocol { _error = error }; } public string Name => "MockHubProtocol"; public int Version => 1; public TransferFormat TransferFormat => TransferFormat.Binary; public bool IsVersionSupported(int version) { return true; } public bool TryParseMessage(ref ReadOnlySequence input, IInvocationBinder binder, out HubMessage message) { if (_error != null) { throw _error; } if (_parsed != null) { message = _parsed; return true; } throw new InvalidOperationException("No Parsed Message provided"); } public void WriteMessage(HubMessage message, IBufferWriter output) { if (_error != null) { throw _error; } } public ReadOnlyMemory GetMessageBytes(HubMessage message) { return HubProtocolExtensions.GetMessageBytes(this, message); } } } }