Moves derived parameter tests into HubConnectionHandlerTests
This commit is contained in:
parent
f80dbd3918
commit
47bb845d48
|
|
@ -661,6 +661,30 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
|||
return output.Reader;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> DerivedParameterInterfaceAsyncEnumerable(IDerivedParameterTestObject param)
|
||||
{
|
||||
await Task.Yield();
|
||||
yield return param.Value;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> DerivedParameterBaseClassAsyncEnumerable(DerivedParameterTestObjectBase param)
|
||||
{
|
||||
await Task.Yield();
|
||||
yield return param.Value;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> DerivedParameterInterfaceAsyncEnumerableWithCancellation(IDerivedParameterTestObject param, [EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
await Task.Yield();
|
||||
yield return param.Value;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> DerivedParameterBaseClassAsyncEnumerableWithCancellation(DerivedParameterTestObjectBase param, [EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
await Task.Yield();
|
||||
yield return param.Value;
|
||||
}
|
||||
|
||||
public class AsyncEnumerableImpl<T> : IAsyncEnumerable<T>
|
||||
{
|
||||
private readonly IAsyncEnumerable<T> _inner;
|
||||
|
|
@ -753,6 +777,18 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDerivedParameterTestObject
|
||||
{
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public abstract class DerivedParameterTestObjectBase : IDerivedParameterTestObject
|
||||
{
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class DerivedParameterTestObject : DerivedParameterTestObjectBase { }
|
||||
}
|
||||
|
||||
public class SimpleHub : Hub
|
||||
|
|
|
|||
|
|
@ -3576,6 +3576,62 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hub methods might be written by users in a way that accepts an interface or base class as a parameter
|
||||
/// and deserialization could supply a derived class.
|
||||
/// This test ensures implementation and subclass arguments are correctly bound for dispatch.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(nameof(StreamingHub.DerivedParameterInterfaceAsyncEnumerable))]
|
||||
[InlineData(nameof(StreamingHub.DerivedParameterBaseClassAsyncEnumerable))]
|
||||
[InlineData(nameof(StreamingHub.DerivedParameterInterfaceAsyncEnumerableWithCancellation))]
|
||||
[InlineData(nameof(StreamingHub.DerivedParameterBaseClassAsyncEnumerableWithCancellation))]
|
||||
public async Task CanPassDerivedParameterToStreamHubMethod(string method)
|
||||
{
|
||||
using (StartVerifiableLog())
|
||||
{
|
||||
var argument = new StreamingHub.DerivedParameterTestObject { Value = "test" };
|
||||
var protocolOptions = new NewtonsoftJsonHubProtocolOptions
|
||||
{
|
||||
PayloadSerializerSettings = new JsonSerializerSettings()
|
||||
{
|
||||
// The usage of TypeNameHandling.All is a security risk.
|
||||
// If you're implementing this in your own application instead use your own 'type' field and a custom JsonConverter
|
||||
// or ensure you're restricting to only known types with a custom SerializationBinder.
|
||||
// See https://github.com/aspnet/AspNetCore/issues/11495#issuecomment-505047422
|
||||
TypeNameHandling = TypeNameHandling.All
|
||||
}
|
||||
};
|
||||
var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(
|
||||
services => services.AddSignalR()
|
||||
.AddNewtonsoftJsonProtocol(o => o.PayloadSerializerSettings = protocolOptions.PayloadSerializerSettings),
|
||||
LoggerFactory);
|
||||
var connectionHandler = serviceProvider.GetService<HubConnectionHandler<StreamingHub>>();
|
||||
var invocationBinder = new Mock<IInvocationBinder>();
|
||||
invocationBinder.Setup(b => b.GetStreamItemType(It.IsAny<string>())).Returns(typeof(string));
|
||||
|
||||
using (var client = new TestClient(
|
||||
protocol: new NewtonsoftJsonHubProtocol(Options.Create(protocolOptions)),
|
||||
invocationBinder: invocationBinder.Object))
|
||||
{
|
||||
var connectionHandlerTask = await client.ConnectAsync(connectionHandler);
|
||||
|
||||
// Wait for a connection, or for the endpoint to fail.
|
||||
await client.Connected.OrThrowIfOtherFails(connectionHandlerTask).OrTimeout();
|
||||
|
||||
var messages = await client.StreamAsync(method, argument).OrTimeout();
|
||||
|
||||
Assert.Equal(2, messages.Count);
|
||||
HubConnectionHandlerTestUtils.AssertHubMessage(new StreamItemMessage(string.Empty, argument.Value), messages[0]);
|
||||
HubConnectionHandlerTestUtils.AssertHubMessage(CompletionMessage.Empty(string.Empty), messages[1]);
|
||||
|
||||
client.Dispose();
|
||||
|
||||
await connectionHandlerTask.OrTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomHubActivator<THub> : IHubActivator<THub> where THub : Hub
|
||||
{
|
||||
public int ReleaseCount;
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
// 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.IO.Pipelines;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.SignalR.Internal;
|
||||
using Microsoft.AspNetCore.SignalR.Protocol;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.SignalR.Tests.Internal
|
||||
{
|
||||
public class DefaultHubDispatcherTests
|
||||
{
|
||||
private class MockHubConnectionContext<TValue> : HubConnectionContext
|
||||
{
|
||||
public TaskCompletionSource<object> ReceivedCompleted = new TaskCompletionSource<object>();
|
||||
public List<TValue> Values = new List<TValue>();
|
||||
|
||||
public MockHubConnectionContext(ConnectionContext connectionContext, HubConnectionContextOptions contextOptions, ILoggerFactory loggerFactory)
|
||||
: base(connectionContext, contextOptions, loggerFactory) { }
|
||||
|
||||
public override ValueTask WriteAsync(HubMessage message, CancellationToken cancellationToken)
|
||||
{
|
||||
if (message is StreamItemMessage streamItemMessage)
|
||||
Values.Add((TValue)streamItemMessage.Item);
|
||||
|
||||
else if (message is CompletionMessage completionMessage)
|
||||
{
|
||||
ReceivedCompleted.TrySetResult(null);
|
||||
|
||||
if (!string.IsNullOrEmpty(completionMessage.Error))
|
||||
{
|
||||
throw new Exception("Error invoking hub method: " + completionMessage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
else throw new NotImplementedException();
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private static DefaultHubDispatcher<THub> CreateDispatcher<THub>() where THub : Hub
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.TryAddScoped(typeof(IHubActivator<>), typeof(DefaultHubActivator<>));
|
||||
var provider = serviceCollection.BuildServiceProvider();
|
||||
var serviceScopeFactory = provider.GetService<IServiceScopeFactory>();
|
||||
|
||||
return new DefaultHubDispatcher<THub>(
|
||||
serviceScopeFactory,
|
||||
new HubContext<THub>(new DefaultHubLifetimeManager<THub>(NullLogger<DefaultHubLifetimeManager<THub>>.Instance)),
|
||||
Options.Create(new HubOptions<THub>()),
|
||||
Options.Create(new HubOptions()),
|
||||
new Logger<DefaultHubDispatcher<THub>>(NullLoggerFactory.Instance));
|
||||
}
|
||||
|
||||
private static MockHubConnectionContext<TValue> CreateConnectionContext<TValue>()
|
||||
{
|
||||
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
|
||||
var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), pair.Application, pair.Transport);
|
||||
var contextOptions = new HubConnectionContextOptions() { KeepAliveInterval = TimeSpan.Zero };
|
||||
|
||||
return new MockHubConnectionContext<TValue>(
|
||||
connection,
|
||||
contextOptions,
|
||||
NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For <see cref="DispatchesDerivedArguments"/>.
|
||||
/// </summary>
|
||||
private interface ITestDerivedParameter
|
||||
{
|
||||
public string Value { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For <see cref="DispatchesDerivedArguments"/>.
|
||||
/// </summary>
|
||||
private abstract class TestDerivedParameterBase
|
||||
{
|
||||
public TestDerivedParameterBase(string value) => Value = value;
|
||||
public string Value { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For <see cref="DispatchesDerivedArguments"/>.
|
||||
/// </summary>
|
||||
private class TestDerivedParameter : TestDerivedParameterBase, ITestDerivedParameter
|
||||
{
|
||||
public TestDerivedParameter(string value) : base(value) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For <see cref="DispatchesDerivedArguments"/>.
|
||||
/// </summary>
|
||||
private class TestDerivedParameterHub : Hub
|
||||
{
|
||||
public async IAsyncEnumerable<string> TestSubclass(TestDerivedParameterBase param, [EnumeratorCancellation]CancellationToken token)
|
||||
{
|
||||
await Task.Yield();
|
||||
yield return param.Value;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> TestImplementation(ITestDerivedParameter param, [EnumeratorCancellation]CancellationToken token)
|
||||
{
|
||||
await Task.Yield();
|
||||
yield return param.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hub methods might be written by users in a way that accepts an interface or base class as a parameter
|
||||
/// and deserialization could supply a derived class (e.g. Json.NET's TypeNameHandling = TypeNameHandling.All).
|
||||
/// This test ensures implementation and subclass arguments are correctly bound for dispatch.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(nameof(TestDerivedParameterHub.TestImplementation))]
|
||||
[InlineData(nameof(TestDerivedParameterHub.TestSubclass))]
|
||||
public async Task DispatchesDerivedArguments(string methodName)
|
||||
{
|
||||
var message = new TestDerivedParameter("Yup");
|
||||
var connectionContext = CreateConnectionContext<string>();
|
||||
var dispatcher = CreateDispatcher<TestDerivedParameterHub>();
|
||||
|
||||
await dispatcher.DispatchMessageAsync(connectionContext, new StreamInvocationMessage("123", methodName, new[] { message }));
|
||||
await (connectionContext as MockHubConnectionContext<string>).ReceivedCompleted.Task;
|
||||
|
||||
Assert.Single(connectionContext.Values, message.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue