139 lines
4.9 KiB
C#
139 lines
4.9 KiB
C#
using System.Buffers;
|
|
using System.Security.Claims;
|
|
using AyCode.Core;
|
|
using AyCode.Core.Extensions;
|
|
using AyCode.Core.Serializers;
|
|
using AyCode.Core.Serializers.Binaries;
|
|
using AyCode.Core.Tests.TestModels;
|
|
using AyCode.Models.Server.DynamicMethods;
|
|
using AyCode.Services.Server.SignalRs;
|
|
using AyCode.Services.SignalRs;
|
|
using Microsoft.AspNetCore.SignalR.Protocol;
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
namespace AyCode.Services.Server.Tests.SignalRs;
|
|
|
|
/// <summary>
|
|
/// Testable SignalR hub that overrides infrastructure dependencies.
|
|
/// Enables unit testing without SignalR server or mocks.
|
|
/// Routes responses through AyCodeBinaryHubProtocol with multi-segment splitting
|
|
/// to exercise the full serialization/deserialization pipeline.
|
|
/// </summary>
|
|
public class TestableSignalRHub2 : AcWebSignalRHubBase<TestSignalRTags, TestLogger>
|
|
{
|
|
private IAcSignalRHubItemServer _callerClient;
|
|
private readonly TestMultiSegmentProtocol _protocol;
|
|
private readonly TestInvocationBinder _binder = new();
|
|
|
|
#region Test Configuration
|
|
|
|
/// <summary>
|
|
/// Simulated connection ID
|
|
/// </summary>
|
|
public string TestConnectionId { get; set; } = "test-connection-id";
|
|
|
|
/// <summary>
|
|
/// Simulated user identifier
|
|
/// </summary>
|
|
public string? TestUserIdentifier { get; set; } = "test-user-id";
|
|
|
|
/// <summary>
|
|
/// Simulated connection aborted state
|
|
/// </summary>
|
|
public bool TestIsConnectionAborted { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Simulated ClaimsPrincipal (optional)
|
|
/// </summary>
|
|
public ClaimsPrincipal? TestUser { get; set; }
|
|
|
|
#endregion
|
|
|
|
public TestableSignalRHub2(BinaryProtocolMode mode = BinaryProtocolMode.Bytes)
|
|
: base(new ConfigurationBuilder().Build(), new TestLogger())
|
|
{
|
|
_protocol = new TestMultiSegmentProtocol(mode);
|
|
}
|
|
|
|
public TestableSignalRHub2(IConfiguration configuration, TestLogger logger, BinaryProtocolMode mode = BinaryProtocolMode.Bytes)
|
|
: base(configuration, logger)
|
|
{
|
|
_protocol = new TestMultiSegmentProtocol(mode);
|
|
}
|
|
|
|
#region Public Test Entry Points
|
|
|
|
/// <summary>
|
|
/// Register a service with SignalR-attributed methods
|
|
/// </summary>
|
|
public void RegisterService(object service, IAcSignalRHubItemServer callerClient)
|
|
{
|
|
_callerClient = callerClient;
|
|
DynamicMethodRegistry.Register(service);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the serializer type for testing Binary vs JSON serialization.
|
|
/// </summary>
|
|
public void SetSerializerType(AcSerializerOptions acSerializerOptions)
|
|
{
|
|
SerializerOptions = acSerializerOptions;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Overridden Context Accessors
|
|
|
|
protected override string GetConnectionId() => TestConnectionId;
|
|
|
|
protected override bool IsConnectionAborted() => TestIsConnectionAborted;
|
|
|
|
protected override string? GetUserIdentifier() => TestUserIdentifier;
|
|
|
|
protected override ClaimsPrincipal? GetUser() => TestUser;
|
|
|
|
#endregion
|
|
|
|
#region Overridden Response Methods
|
|
|
|
protected override Task ResponseToCaller(int messageTag, SignalResponseStatus status, object? responseData, int? requestId, SignalParams? clientSignalParams = null)
|
|
=> SendMessageToClient(_callerClient, messageTag, status, responseData, requestId, clientSignalParams);
|
|
|
|
protected override Task SendMessageToClient(IAcSignalRHubItemServer sendTo, int messageTag,
|
|
SignalResponseStatus status, object? responseData, int? requestId = null, SignalParams? clientSignalParams = null)
|
|
{
|
|
var isRawBytes = clientSignalParams?.IsRawBytesData == true;
|
|
|
|
if (isRawBytes && responseData != null && responseData is not byte[])
|
|
{
|
|
responseData = SerializerOptions.SerializerType == AcSerializerType.Binary
|
|
? AcBinarySerializer.Serialize(responseData)
|
|
: AyCode.Core.Compression.GzipHelper.Compress(responseData.ToJson());
|
|
}
|
|
|
|
var signalParams = new SignalParams
|
|
{
|
|
Status = status,
|
|
DataSerializerType = SerializerOptions.SerializerType,
|
|
SignalDataType = isRawBytes ? null : responseData?.GetType().AssemblyQualifiedName,
|
|
IsRawBytesData = isRawBytes
|
|
};
|
|
|
|
//TODO: átrakni a protocl-ra és itt csak beadjuk a protocol-nak! a reader esetbében is olvasáskor!
|
|
// Protocol round-trip: serialize → multi-segment split → deserialize
|
|
var invocation = new InvocationMessage(
|
|
nameof(IAcSignalRHubClient.OnReceiveMessage),
|
|
[messageTag, requestId, signalParams, responseData ?? Array.Empty<byte>()]);
|
|
|
|
var bytes = _protocol.GetMessageBytesMultiSegment(invocation);
|
|
var sequence = new ReadOnlySequence<byte>(bytes);
|
|
_protocol.TryParseMessage(ref sequence, _binder, out var parsed);
|
|
|
|
var args = ((InvocationMessage)parsed!).Arguments;
|
|
return sendTo.OnReceiveMessage(
|
|
(int)args[0]!, (int?)args[1], (SignalParams)args[2]!, args[3]!);
|
|
}
|
|
|
|
#endregion
|
|
}
|