Create a Public Test Suite for HubLifetimeManager implementations (#2353)

This commit is contained in:
Mikael Mengistu 2018-06-06 16:59:55 -07:00 committed by GitHub
parent 4a34b879dd
commit 648705f648
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 647 additions and 737 deletions

View File

@ -89,6 +89,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crankier", "benchmarkapps\C
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarkapps", "benchmarkapps", "{43F352F3-4E2B-4ED7-901B-36E6671251F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Specification.Tests", "src\Microsoft.AspNetCore.SignalR.Specification.Tests\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj", "{2B03333F-3ACD-474C-862B-FA97D3BA03B5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -207,6 +209,10 @@ Global
{8D3E3E7D-452B-44F4-86CA-111003EA11ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D3E3E7D-452B-44F4-86CA-111003EA11ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D3E3E7D-452B-44F4-86CA-111003EA11ED}.Release|Any CPU.Build.0 = Release|Any CPU
{2B03333F-3ACD-474C-862B-FA97D3BA03B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B03333F-3ACD-474C-862B-FA97D3BA03B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B03333F-3ACD-474C-862B-FA97D3BA03B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B03333F-3ACD-474C-862B-FA97D3BA03B5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -240,6 +246,7 @@ Global
{896FA5EE-63A5-4EAC-9F09-346584BB4830} = {DA69F624-5398-4884-87E4-B816698CDE65}
{8C75AC94-C980-4FE1-9F79-6CED3C8665CE} = {43F352F3-4E2B-4ED7-901B-36E6671251F5}
{8D3E3E7D-452B-44F4-86CA-111003EA11ED} = {43F352F3-4E2B-4ED7-901B-36E6671251F5}
{2B03333F-3ACD-474C-862B-FA97D3BA03B5} = {DA69F624-5398-4884-87E4-B816698CDE65}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7945A4E4-ACDB-4F6E-95CA-6AC6E7C2CD59}

View File

@ -11,4 +11,4 @@
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
<PackageReference Include="Internal.AspNetCore.Analyzers" PrivateAssets="All" Version="$(InternalAspNetCoreAnalyzersPackageVersion)" />
</ItemGroup>
</Project>
</Project>

View File

@ -23,4 +23,4 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
</ItemGroup>
</Project>
</Project>

View File

@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.SignalR
/// <summary>
/// Gets the protocol used by this connection.
/// </summary>
public virtual IHubProtocol Protocol { get; internal set; }
public virtual IHubProtocol Protocol { get; set; }
// Currently used only for streaming methods
internal ConcurrentDictionary<string, CancellationTokenSource> ActiveRequestCancellationSources { get; } = new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.Ordinal);

View File

@ -0,0 +1,142 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.AspNetCore.SignalR.Tests;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Specification.Tests
{
public abstract class HubLifetimeManagerTestsBase<THub> where THub : Hub
{
public HubLifetimeManager<THub> Manager { get; set; }
public abstract HubLifetimeManager<THub> CreateNewHubLifetimeManager();
[Fact]
public async Task SendAllAsyncWritesToAllConnectionsOutput()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateNewHubLifetimeManager();
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
message = Assert.IsType<InvocationMessage>(client2.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
}
}
[Fact]
public async Task SendAllAsyncDoesNotWriteToDisconnectedConnectionsOutput()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateNewHubLifetimeManager();
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.OnDisconnectedAsync(connection2).OrTimeout();
await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task SendGroupAsyncWritesToAllConnectionsInGroupOutput()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateNewHubLifetimeManager();
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "group").OrTimeout();
await manager.SendGroupAsync("group", "Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task SendGroupExceptAsyncDoesNotWriteToExcludedConnections()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateNewHubLifetimeManager();
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "group1").OrTimeout();
await manager.AddToGroupAsync(connection2.ConnectionId, "group1").OrTimeout();
await manager.SendGroupExceptAsync("group1", "Hello", new object[] { "World" }, new[] { connection2.ConnectionId }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task SendConnectionAsyncWritesToConnectionOutput()
{
using (var client = new TestClient())
{
var manager = CreateNewHubLifetimeManager();
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
}
}
}
}

View File

@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Tests for users to verify their own implementations of SignalR types</Description>
<DeveloperBuildTestTfms>netcoreapp2.2</DeveloperBuildTestTfms>
<StandardTestTfms>$(DeveloperBuildTestTfms)</StandardTestTfms>
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">$(StandardTestTfms);net461</StandardTestTfms>
<RuntimeIdentifier Condition="'$(TargetFramework)' == 'net461'">win7-x86</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Common\DuplexPipe.cs" Link="DuplexPipe.cs" />
<Compile Include="..\Common\MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
<Compile Include="..\..\test\Microsoft.AspNetCore.SignalR.Tests.Utils\HubConnectionContextUtils.cs" Link="HubConnectionContextUtils.cs" />
<Compile Include="..\..\test\Microsoft.AspNetCore.SignalR.Tests.Utils\TaskExtensions.cs" Link="TaskExtensions.cs" />
<Compile Include="..\..\test\Microsoft.AspNetCore.SignalR.Tests.Utils\TestClient.cs" Link="TestClient.cs" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Core\Microsoft.AspNetCore.SignalR.Core.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Protocols.MessagePack\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,399 @@
// 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.SignalR.Protocol;
using Microsoft.AspNetCore.SignalR.Tests;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Specification.Tests
{
public abstract class ScaleoutHubLifetimeManagerTests<TBackplane> : HubLifetimeManagerTestsBase<MyHub>
{
public abstract TBackplane CreateBackplane();
public abstract HubLifetimeManager<MyHub> CreateNewHubLifetimeManager(TBackplane backplane);
private async Task AssertMessageAsync(TestClient client)
{
var message = Assert.IsType<InvocationMessage>(await client.ReadAsync().OrTimeout());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
}
[Fact]
public async Task InvokeAllAsyncWithMultipleServersWritesToAllConnectionsOutput()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager1.OnConnectedAsync(connection1).OrTimeout();
await manager2.OnConnectedAsync(connection2).OrTimeout();
await manager1.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
}
}
[Fact]
public async Task InvokeAllAsyncWithMultipleServersDoesNotWriteToDisconnectedConnectionsOutput()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager1.OnConnectedAsync(connection1).OrTimeout();
await manager2.OnConnectedAsync(connection2).OrTimeout();
await manager2.OnDisconnectedAsync(connection2).OrTimeout();
await manager2.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task InvokeConnectionAsyncOnServerWithoutConnectionWritesOutputToConnection()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
}
}
[Fact]
public async Task InvokeGroupAsyncOnServerWithoutConnectionWritesOutputToGroupConnection()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
}
}
[Fact]
public async Task DisconnectConnectionRemovesConnectionFromGroup()
{
var backplane = CreateBackplane();
var manager = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.OnDisconnectedAsync(connection).OrTimeout();
await manager.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task RemoveGroupFromLocalConnectionNotInGroupDoesNothing()
{
var backplane = CreateBackplane();
var manager = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
}
}
[Fact]
public async Task RemoveGroupFromConnectionOnDifferentServerNotInGroupDoesNothing()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
}
}
[Fact]
public async Task AddGroupAsyncForConnectionOnDifferentServerWorks()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
}
}
[Fact]
public async Task AddGroupAsyncForLocalConnectionAlreadyInGroupDoesNothing()
{
var backplane = CreateBackplane();
var manager = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task AddGroupAsyncForConnectionOnDifferentServerAlreadyInGroupDoesNothing()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task RemoveGroupAsyncForConnectionOnDifferentServerWorks()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
await manager2.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task InvokeConnectionAsyncForLocalConnectionDoesNotPublishToBackplane()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
// Add connection to both "servers" to see if connection receives message twice
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.OnConnectedAsync(connection).OrTimeout();
await manager1.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task WritingToRemoteConnectionThatFailsDoesNotThrow()
{
var backplane = CreateBackplane();
var manager1 = CreateNewHubLifetimeManager(backplane);
var manager2 = CreateNewHubLifetimeManager(backplane);
using (var client = new TestClient())
{
// Force an exception when writing to connection
var connectionMock = HubConnectionContextUtils.CreateMock(client.Connection);
connectionMock.Setup(m => m.WriteAsync(It.IsAny<HubMessage>(), It.IsAny<CancellationToken>())).Throws(new Exception());
var connection = connectionMock.Object;
await manager2.OnConnectedAsync(connection).OrTimeout();
// This doesn't throw because there is no connection.ConnectionId on this server so it has to publish to the backplane.
// And once that happens there is no way to know if the invocation was successful or not.
await manager1.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
}
}
[Fact]
public async Task WritingToGroupWithOneConnectionFailingSecondConnectionStillReceivesMessage()
{
var backplane = CreateBackplane();
var manager = CreateNewHubLifetimeManager(backplane);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
// Force an exception when writing to connection
var connectionMock = HubConnectionContextUtils.CreateMock(client1.Connection);
connectionMock.Setup(m => m.WriteAsync(It.IsAny<HubMessage>(), It.IsAny<CancellationToken>())).Throws(new Exception());
var connection1 = connectionMock.Object;
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "group");
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection2.ConnectionId, "group");
await manager.SendGroupAsync("group", "Hello", new object[] { "World" }).OrTimeout();
// connection1 will throw when receiving a group message, we are making sure other connections
// are not affected by another connection throwing
await AssertMessageAsync(client2);
// Repeat to check that group can still be sent to
await manager.SendGroupAsync("group", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client2);
}
}
[Fact]
public async Task InvokeUserSendsToAllConnectionsForUser()
{
var backplane = CreateBackplane();
var manager = CreateNewHubLifetimeManager(backplane);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
using (var client3 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection, userIdentifier: "userA");
var connection2 = HubConnectionContextUtils.Create(client2.Connection, userIdentifier: "userA");
var connection3 = HubConnectionContextUtils.Create(client3.Connection, userIdentifier: "userB");
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.OnConnectedAsync(connection3).OrTimeout();
await manager.SendUserAsync("userA", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
}
}
[Fact]
public async Task StillSubscribedToUserAfterOneOfMultipleConnectionsAssociatedWithUserDisconnects()
{
var backplane = CreateBackplane();
var manager = CreateNewHubLifetimeManager(backplane);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
using (var client3 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection, userIdentifier: "userA");
var connection2 = HubConnectionContextUtils.Create(client2.Connection, userIdentifier: "userA");
var connection3 = HubConnectionContextUtils.Create(client3.Connection, userIdentifier: "userB");
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.OnConnectedAsync(connection3).OrTimeout();
await manager.SendUserAsync("userA", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
// Disconnect one connection for the user
await manager.OnDisconnectedAsync(connection1).OrTimeout();
await manager.SendUserAsync("userA", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client2);
}
}
}
public class MyHub : Hub
{
}
}

View File

@ -20,4 +20,4 @@
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@ -15,6 +15,7 @@
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR\Microsoft.AspNetCore.SignalR.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Client\Microsoft.AspNetCore.SignalR.Client.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Specification.Tests\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -1,558 +1,58 @@
// 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.Collections.Generic;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using Microsoft.AspNetCore.SignalR.Specification.Tests;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Redis.Tests
{
public class RedisHubLifetimeManagerTests
public class RedisHubLifetimeManagerTests : ScaleoutHubLifetimeManagerTests<TestRedisServer>
{
[Fact]
public async Task InvokeAllAsyncWritesToAllConnectionsOutput()
private TestRedisServer _server;
public override HubLifetimeManager<MyHub> CreateNewHubLifetimeManager(TestRedisServer backplane)
{
var server = new TestRedisServer();
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateLifetimeManager(server);
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
}
return CreateLifetimeManager(backplane);
}
[Fact]
public async Task InvokeAllExceptAsyncExcludesSpecifiedConnections()
public override TestRedisServer CreateBackplane()
{
var server = new TestRedisServer();
using (var client1 = new TestClient())
using (var client2 = new TestClient())
using (var client3 = new TestClient())
{
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
var manager3 = CreateLifetimeManager(server);
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
var connection3 = HubConnectionContextUtils.Create(client3.Connection);
await manager1.OnConnectedAsync(connection1).OrTimeout();
await manager2.OnConnectedAsync(connection2).OrTimeout();
await manager3.OnConnectedAsync(connection3).OrTimeout();
await manager1.SendAllExceptAsync("Hello", new object[] { "World" }, new[] { client3.Connection.ConnectionId }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
Assert.Null(client3.TryRead());
}
return new TestRedisServer();
}
[Fact]
public async Task InvokeAllAsyncDoesNotWriteToDisconnectedConnectionsOutput()
public override HubLifetimeManager<MyHub> CreateNewHubLifetimeManager()
{
var server = new TestRedisServer();
_server = new TestRedisServer();
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateLifetimeManager(server);
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.OnDisconnectedAsync(connection2).OrTimeout();
await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
Assert.Null(client2.TryRead());
}
return CreateLifetimeManager(_server);
}
[Fact]
public async Task InvokeGroupAsyncWritesToAllConnectionsInGroupOutput()
public class TestObject
{
var server = new TestRedisServer();
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateLifetimeManager(server);
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "gunit").OrTimeout();
await manager.SendGroupAsync("gunit", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
Assert.Null(client2.TryRead());
}
public string TestProperty { get; set; }
}
[Fact]
public async Task InvokeGroupExceptAsyncWritesToAllValidConnectionsInGroupOutput()
private RedisHubLifetimeManager<MyHub> CreateLifetimeManager(TestRedisServer server, MessagePackHubProtocolOptions messagePackOptions = null, JsonHubProtocolOptions jsonOptions = null)
{
var server = new TestRedisServer();
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = CreateLifetimeManager(server);
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "gunit").OrTimeout();
await manager.AddToGroupAsync(connection2.ConnectionId, "gunit").OrTimeout();
var excludedConnectionIds = new List<string> { client2.Connection.ConnectionId };
await manager.SendGroupExceptAsync("gunit", "Hello", new object[] { "World" }, excludedConnectionIds).OrTimeout();
await AssertMessageAsync(client1);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task InvokeConnectionAsyncWritesToConnectionOutput()
{
var server = new TestRedisServer();
using (var client = new TestClient())
{
var manager = CreateLifetimeManager(server);
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
}
}
[Fact]
public async Task InvokeConnectionAsyncOnNonExistentConnectionDoesNotThrow()
{
var server = new TestRedisServer();
var manager = CreateLifetimeManager(server);
await manager.SendConnectionAsync("NotARealConnectionId", "Hello", new object[] { "World" }).OrTimeout();
}
[Fact]
public async Task InvokeAllAsyncWithMultipleServersWritesToAllConnectionsOutput()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager1.OnConnectedAsync(connection1).OrTimeout();
await manager2.OnConnectedAsync(connection2).OrTimeout();
await manager1.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
}
}
[Fact]
public async Task InvokeAllAsyncWithMultipleServersDoesNotWriteToDisconnectedConnectionsOutput()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager1.OnConnectedAsync(connection1).OrTimeout();
await manager2.OnConnectedAsync(connection2).OrTimeout();
await manager2.OnDisconnectedAsync(connection2).OrTimeout();
await manager2.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task InvokeConnectionAsyncOnServerWithoutConnectionWritesOutputToConnection()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
}
}
[Fact]
public async Task InvokeGroupAsyncOnServerWithoutConnectionWritesOutputToGroupConnection()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
}
}
[Fact]
public async Task DisconnectConnectionRemovesConnectionFromGroup()
{
var server = new TestRedisServer();
var manager = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.OnDisconnectedAsync(connection).OrTimeout();
await manager.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task RemoveGroupFromLocalConnectionNotInGroupDoesNothing()
{
var server = new TestRedisServer();
var manager = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
}
}
[Fact]
public async Task RemoveGroupFromConnectionOnDifferentServerNotInGroupDoesNothing()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
}
}
[Fact]
public async Task AddGroupAsyncForConnectionOnDifferentServerWorks()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
}
}
[Fact]
public async Task AddGroupAsyncForLocalConnectionAlreadyInGroupDoesNothing()
{
var server = new TestRedisServer();
var manager = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task AddGroupAsyncForConnectionOnDifferentServerAlreadyInGroupDoesNothing()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task RemoveGroupAsyncForConnectionOnDifferentServerWorks()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
await manager2.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task InvokeConnectionAsyncForLocalConnectionDoesNotPublishToRedis()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
var connection = HubConnectionContextUtils.Create(client.Connection);
// Add connection to both "servers" to see if connection receives message twice
await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.OnConnectedAsync(connection).OrTimeout();
await manager1.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client);
Assert.Null(client.TryRead());
}
}
[Fact]
public async Task WritingToRemoteConnectionThatFailsDoesNotThrow()
{
var server = new TestRedisServer();
var manager1 = CreateLifetimeManager(server);
var manager2 = CreateLifetimeManager(server);
using (var client = new TestClient())
{
// Force an exception when writing to connection
var connectionMock = HubConnectionContextUtils.CreateMock(client.Connection);
connectionMock.Setup(m => m.WriteAsync(It.IsAny<HubMessage>(), It.IsAny<CancellationToken>())).Throws(new Exception());
var connection = connectionMock.Object;
await manager2.OnConnectedAsync(connection).OrTimeout();
// This doesn't throw because there is no connection.ConnectionId on this server so it has to publish to redis.
// And once that happens there is no way to know if the invocation was successful or not.
await manager1.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
}
}
[Fact]
public async Task WritingToGroupWithOneConnectionFailingSecondConnectionStillReceivesMessage()
{
var server = new TestRedisServer();
var manager = CreateLifetimeManager(server);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
// Force an exception when writing to connection
var connectionMock = HubConnectionContextUtils.CreateMock(client1.Connection);
connectionMock.Setup(m => m.WriteAsync(It.IsAny<HubMessage>(), It.IsAny<CancellationToken>())).Throws(new Exception());
var connection1 = connectionMock.Object;
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "group");
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection2.ConnectionId, "group");
await manager.SendGroupAsync("group", "Hello", new object[] { "World" }).OrTimeout();
// connection1 will throw when receiving a group message, we are making sure other connections
// are not affected by another connection throwing
await AssertMessageAsync(client2);
// Repeat to check that group can still be sent to
await manager.SendGroupAsync("group", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client2);
}
}
[Fact]
public async Task InvokeUserSendsToAllConnectionsForUser()
{
var server = new TestRedisServer();
var manager = CreateLifetimeManager(server);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
using (var client3 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection, userIdentifier: "userA");
var connection2 = HubConnectionContextUtils.Create(client2.Connection, userIdentifier: "userA");
var connection3 = HubConnectionContextUtils.Create(client3.Connection, userIdentifier: "userB");
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.OnConnectedAsync(connection3).OrTimeout();
await manager.SendUserAsync("userA", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
}
}
[Fact]
public async Task StillSubscribedToUserAfterOneOfMultipleConnectionsAssociatedWithUserDisconnects()
{
var server = new TestRedisServer();
var manager = CreateLifetimeManager(server);
using (var client1 = new TestClient())
using (var client2 = new TestClient())
using (var client3 = new TestClient())
{
var connection1 = HubConnectionContextUtils.Create(client1.Connection, userIdentifier: "userA");
var connection2 = HubConnectionContextUtils.Create(client2.Connection, userIdentifier: "userA");
var connection3 = HubConnectionContextUtils.Create(client3.Connection, userIdentifier: "userB");
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.OnConnectedAsync(connection3).OrTimeout();
await manager.SendUserAsync("userA", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client1);
await AssertMessageAsync(client2);
// Disconnect one connection for the user
await manager.OnDisconnectedAsync(connection1).OrTimeout();
await manager.SendUserAsync("userA", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client2);
}
var options = new RedisOptions() { ConnectionFactory = async (t) => await Task.FromResult(new TestConnectionMultiplexer(server)) };
messagePackOptions = messagePackOptions ?? new MessagePackHubProtocolOptions();
jsonOptions = jsonOptions ?? new JsonHubProtocolOptions();
return new RedisHubLifetimeManager<MyHub>(
NullLogger<RedisHubLifetimeManager<MyHub>>.Instance,
Options.Create(options),
new DefaultHubProtocolResolver(new IHubProtocol[]
{
new JsonHubProtocol(Options.Create(jsonOptions)),
new MessagePackHubProtocol(Options.Create(messagePackOptions)),
}, NullLogger<DefaultHubProtocolResolver>.Instance));
}
[Fact]
@ -598,46 +98,5 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
});
}
}
public class TestObject
{
public string TestProperty { get; set; }
}
private RedisHubLifetimeManager<MyHub> CreateLifetimeManager(TestRedisServer server, MessagePackHubProtocolOptions messagePackOptions = null, JsonHubProtocolOptions jsonOptions = null)
{
var options = new RedisOptions() { ConnectionFactory = async (t) => await Task.FromResult(new TestConnectionMultiplexer(server)) };
messagePackOptions = messagePackOptions ?? new MessagePackHubProtocolOptions();
jsonOptions = jsonOptions ?? new JsonHubProtocolOptions();
return new RedisHubLifetimeManager<MyHub>(
NullLogger<RedisHubLifetimeManager<MyHub>>.Instance,
Options.Create(options),
new DefaultHubProtocolResolver(new IHubProtocol[]
{
new JsonHubProtocol(Options.Create(jsonOptions)),
new MessagePackHubProtocol(Options.Create(messagePackOptions)),
}, NullLogger<DefaultHubProtocolResolver>.Instance));
}
private async Task AssertMessageAsync(TestClient client)
{
var message = Assert.IsType<InvocationMessage>(await client.ReadAsync().OrTimeout());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
}
private class MyHub : Hub
{
}
private class MockChannel : Channel<HubMessage>
{
public MockChannel(ChannelWriter<HubMessage> writer = null)
{
Writer = writer;
}
}
}
}

View File

@ -9,7 +9,12 @@ using Moq;
namespace Microsoft.AspNetCore.SignalR.Tests
{
public static class HubConnectionContextUtils
#if TESTUTILS
public
#else
internal
#endif
static class HubConnectionContextUtils
{
public static HubConnectionContext Create(ConnectionContext connection, IHubProtocol protocol = null, string userIdentifier = null)
{

View File

@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<RootNamespace>Microsoft.AspNetCore.SignalR.Tests</RootNamespace>
<DefineConstants>$(DefineConstants);TESTUTILS</DefineConstants>
</PropertyGroup>
<ItemGroup>

View File

@ -7,7 +7,12 @@ using System.Runtime.CompilerServices;
namespace System.Threading.Tasks
{
public static class TaskExtensions
#if TESTUTILS
public
#else
internal
#endif
static class TaskExtensions
{
private const int DefaultTimeout = 5000;

View File

@ -14,7 +14,12 @@ using Microsoft.AspNetCore.SignalR.Protocol;
namespace Microsoft.AspNetCore.SignalR.Tests
{
public class TestClient : ITransferFormatFeature, IConnectionHeartbeatFeature, IDisposable
#if TESTUTILS
public
#else
internal
#endif
class TestClient : ITransferFormatFeature, IConnectionHeartbeatFeature, IDisposable
{
private readonly object _heartbeatLock = new object();
private List<(Action<object> handler, object state)> _heartbeatHandlers;

View File

@ -1,172 +1,17 @@
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Protocol;
// 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 Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
using Microsoft.AspNetCore.SignalR.Specification.Tests;
namespace Microsoft.AspNetCore.SignalR.Tests
{
public class DefaultHubLifetimeManagerTests
public class DefaultHubLifetimeManagerTests : HubLifetimeManagerTestsBase<MyHub>
{
[Fact]
public async Task SendAllAsyncWritesToAllConnectionsOutput()
public override HubLifetimeManager<MyHub> CreateNewHubLifetimeManager()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
message = Assert.IsType<InvocationMessage>(client2.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
}
}
[Fact]
public async Task SendAllAsyncDoesNotWriteToDisconnectedConnectionsOutput()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.OnDisconnectedAsync(connection2).OrTimeout();
await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task SendGroupAsyncWritesToAllConnectionsInGroupOutput()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "gunit").OrTimeout();
await manager.SendGroupAsync("gunit", "Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task SendGroupExceptAsyncDoesNotWriteToExcludedConnections()
{
using (var client1 = new TestClient())
using (var client2 = new TestClient())
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
var connection1 = HubConnectionContextUtils.Create(client1.Connection);
var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddToGroupAsync(connection1.ConnectionId, "gunit").OrTimeout();
await manager.AddToGroupAsync(connection2.ConnectionId, "gunit").OrTimeout();
await manager.SendGroupExceptAsync("gunit", "Hello", new object[] { "World" }, new []{ connection2.ConnectionId }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client1.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
Assert.Null(client2.TryRead());
}
}
[Fact]
public async Task SendConnectionAsyncWritesToConnectionOutput()
{
using (var client = new TestClient())
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
var connection = HubConnectionContextUtils.Create(client.Connection);
await manager.OnConnectedAsync(connection).OrTimeout();
await manager.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
var message = Assert.IsType<InvocationMessage>(client.TryRead());
Assert.Equal("Hello", message.Target);
Assert.Single(message.Arguments);
Assert.Equal("World", (string)message.Arguments[0]);
}
}
[Fact]
public async Task SendConnectionAsyncOnNonExistentConnectionNoops()
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
await manager.SendConnectionAsync("NotARealConnectionId", "Hello", new object[] { "World" }).OrTimeout();
}
[Fact]
public async Task AddGroupOnNonExistentConnectionNoops()
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
await manager.AddToGroupAsync("NotARealConnectionId", "MyGroup").OrTimeout();
}
[Fact]
public async Task RemoveGroupOnNonExistentConnectionNoops()
{
var manager = new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
await manager.RemoveFromGroupAsync("NotARealConnectionId", "MyGroup").OrTimeout();
}
private class MyHub : Hub
{
}
private class MockChannel: Channel<HubMessage>
{
public MockChannel(ChannelWriter<HubMessage> writer = null)
{
Writer = writer;
}
return new DefaultHubLifetimeManager<MyHub>(new Logger<DefaultHubLifetimeManager<MyHub>>(NullLoggerFactory.Instance));
}
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
@ -24,6 +24,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR\Microsoft.AspNetCore.SignalR.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Client\Microsoft.AspNetCore.SignalR.Client.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Specification.Tests\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj" />
</ItemGroup>