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; /// /// 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. /// public class TestableSignalRHub2 : AcWebSignalRHubBase { private IAcSignalRHubItemServer _callerClient; private readonly TestMultiSegmentProtocol _protocol; private readonly TestInvocationBinder _binder = new(); #region Test Configuration /// /// Simulated connection ID /// public string TestConnectionId { get; set; } = "test-connection-id"; /// /// Simulated user identifier /// public string? TestUserIdentifier { get; set; } = "test-user-id"; /// /// Simulated connection aborted state /// public bool TestIsConnectionAborted { get; set; } = false; /// /// Simulated ClaimsPrincipal (optional) /// 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 /// /// Register a service with SignalR-attributed methods /// public void RegisterService(object service, IAcSignalRHubItemServer callerClient) { _callerClient = callerClient; DynamicMethodRegistry.Register(service); } /// /// Sets the serializer type for testing Binary vs JSON serialization. /// 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, 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()]); var bytes = _protocol.GetMessageBytesMultiSegment(invocation); var sequence = new ReadOnlySequence(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 }