using AyCode.Core.Tests.TestModels;
using AyCode.Core.Extensions;
using AyCode.Services.SignalRs;
using MessagePack;
namespace AyCode.Services.Server.Tests.SignalRs;
///
/// Tests for AcWebSignalRHubBase.ProcessOnReceiveMessage.
/// Uses shared DTOs from AyCode.Core.Tests.TestModels.
///
[TestClass]
public class ProcessOnReceiveMessageTests
{
private TestableSignalRHub _hub = null!;
private TestSignalRService _service = null!;
[TestInitialize]
public void Setup()
{
_hub = new TestableSignalRHub();
_service = new TestSignalRService();
_hub.RegisterService(_service);
}
[TestCleanup]
public void Cleanup()
{
_hub.Reset();
_service.Reset();
}
#region Single Primitive Parameter Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_SingleInt_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(42);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.SingleIntParam, message);
// Assert
Assert.IsTrue(_service.SingleIntMethodCalled);
Assert.AreEqual(42, _service.ReceivedInt);
Assert.AreEqual(1, _hub.SentMessages.Count);
SignalRTestHelper.AssertSuccessResponse(_hub.SentMessages[0], TestSignalRTags.SingleIntParam);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_BoolTrue_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(true);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.BoolParam, message);
// Assert
Assert.IsTrue(_service.BoolMethodCalled);
Assert.IsTrue(_service.ReceivedBool);
var responseData = SignalRTestHelper.GetResponseData(_hub.SentMessages[0]);
Assert.IsTrue(responseData, "Response should be true");
}
[TestMethod]
public async Task ProcessOnReceiveMessage_BoolFalse_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(false);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.BoolParam, message);
// Assert
Assert.IsTrue(_service.BoolMethodCalled);
Assert.IsFalse(_service.ReceivedBool);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_String_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage("Hello SignalR!");
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.StringParam, message);
// Assert
Assert.IsTrue(_service.StringMethodCalled);
Assert.AreEqual("Hello SignalR!", _service.ReceivedString);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_Guid_DeserializesCorrectly()
{
// Arrange
var testGuid = Guid.NewGuid();
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(testGuid);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.GuidParam, message);
// Assert
Assert.IsTrue(_service.GuidMethodCalled);
Assert.AreEqual(testGuid, _service.ReceivedGuid);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_Enum_DeserializesCorrectly()
{
// Arrange - using shared TestStatus enum
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(TestStatus.Active);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.EnumParam, message);
// Assert
Assert.IsTrue(_service.EnumMethodCalled);
Assert.AreEqual(TestStatus.Active, _service.ReceivedEnum);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_Decimal_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(123.456m);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DecimalParam, message);
// Assert
Assert.IsTrue(_service.DecimalMethodCalled);
Assert.AreEqual(123.456m, _service.ReceivedDecimal);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_DateTime_DeserializesCorrectly()
{
// Arrange
var testDate = new DateTime(2024, 12, 25, 12, 30, 45, DateTimeKind.Utc);
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(testDate);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DateTimeParam, message);
// Assert
Assert.IsTrue(_service.DateTimeMethodCalled);
Assert.AreEqual(testDate, _service.ReceivedDateTime);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_Double_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(3.14159265359);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DoubleParam, message);
// Assert
Assert.IsTrue(_service.DoubleMethodCalled);
Assert.IsNotNull(_service.ReceivedDouble);
Assert.IsTrue(Math.Abs(_service.ReceivedDouble.Value - 3.14159265359) < 0.0000000001);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_Long_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(9223372036854775807L);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.LongParam, message);
// Assert
Assert.IsTrue(_service.LongMethodCalled);
Assert.AreEqual(9223372036854775807L, _service.ReceivedLong);
}
#endregion
#region Multiple Primitive Parameters Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_TwoInts_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreatePrimitiveParamsMessage(10, 20);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.TwoIntParams, message);
// Assert
Assert.IsTrue(_service.TwoIntMethodCalled);
Assert.AreEqual((10, 20), _service.ReceivedTwoInts);
var responseData = SignalRTestHelper.GetResponseData(_hub.SentMessages[0]);
Assert.AreEqual(30, responseData, "Sum should be 30");
}
[TestMethod]
public async Task ProcessOnReceiveMessage_MultipleTypes_DeserializesCorrectly()
{
// Arrange
var message = SignalRTestHelper.CreatePrimitiveParamsMessage(true, "test", 123);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.MultipleTypesParams, message);
// Assert
Assert.IsTrue(_service.MultipleTypesMethodCalled);
Assert.AreEqual((true, "test", 123), _service.ReceivedMultipleTypes);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_FiveParams_DeserializesCorrectly()
{
// Arrange
var testGuid = Guid.NewGuid();
var message = SignalRTestHelper.CreatePrimitiveParamsMessage(42, "hello", true, testGuid, 99.99m);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.FiveParams, message);
// Assert
Assert.IsTrue(_service.FiveParamsMethodCalled);
Assert.IsNotNull(_service.ReceivedFiveParams);
Assert.AreEqual(42, _service.ReceivedFiveParams.Value.Item1);
Assert.AreEqual("hello", _service.ReceivedFiveParams.Value.Item2);
Assert.AreEqual(true, _service.ReceivedFiveParams.Value.Item3);
Assert.AreEqual(testGuid, _service.ReceivedFiveParams.Value.Item4);
Assert.AreEqual(99.99m, _service.ReceivedFiveParams.Value.Item5);
}
#endregion
#region Complex Object Tests (using shared DTOs)
[TestMethod]
public async Task ProcessOnReceiveMessage_TestOrderItem_DeserializesCorrectly()
{
// Arrange - using shared TestOrderItem from Core.Tests
var item = new TestOrderItem
{
Id = 1,
ProductName = "Test Product",
Quantity = 10,
UnitPrice = 99.99m,
Status = TestStatus.Active
};
var message = SignalRTestHelper.CreateComplexObjectMessage(item);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.TestOrderItemParam, message);
// Assert
Assert.IsTrue(_service.TestOrderItemMethodCalled);
Assert.IsNotNull(_service.ReceivedTestOrderItem);
Assert.AreEqual(1, _service.ReceivedTestOrderItem.Id);
Assert.AreEqual("Test Product", _service.ReceivedTestOrderItem.ProductName);
Assert.AreEqual(10, _service.ReceivedTestOrderItem.Quantity);
Assert.AreEqual(99.99m, _service.ReceivedTestOrderItem.UnitPrice);
Assert.AreEqual(TestStatus.Active, _service.ReceivedTestOrderItem.Status);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_TestOrder_WithNestedItems_DeserializesCorrectly()
{
// Arrange - using shared TestOrder with nested items
var order = new TestOrder
{
Id = 100,
OrderNumber = "ORD-001",
Status = TestStatus.Active,
TotalAmount = 500.00m,
Items =
[
new TestOrderItem { Id = 1, ProductName = "Item A", Quantity = 2 },
new TestOrderItem { Id = 2, ProductName = "Item B", Quantity = 3 }
]
};
var message = SignalRTestHelper.CreateComplexObjectMessage(order);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.TestOrderParam, message);
// Assert
Assert.IsTrue(_service.TestOrderMethodCalled);
Assert.IsNotNull(_service.ReceivedTestOrder);
Assert.AreEqual(100, _service.ReceivedTestOrder.Id);
Assert.AreEqual("ORD-001", _service.ReceivedTestOrder.OrderNumber);
Assert.AreEqual(2, _service.ReceivedTestOrder.Items.Count);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_SharedTag_IIdType_DeserializesCorrectly()
{
// Arrange - using shared SharedTag (IId type)
var tag = new SharedTag
{
Id = 1,
Name = "Important",
Color = "#FF0000",
Priority = 1,
IsActive = true
};
var message = SignalRTestHelper.CreateComplexObjectMessage(tag);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.SharedTagParam, message);
// Assert
Assert.IsTrue(_service.SharedTagMethodCalled);
Assert.IsNotNull(_service.ReceivedSharedTag);
Assert.AreEqual(1, _service.ReceivedSharedTag.Id);
Assert.AreEqual("Important", _service.ReceivedSharedTag.Name);
Assert.AreEqual("#FF0000", _service.ReceivedSharedTag.Color);
}
#endregion
#region No Parameters Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_NoParams_InvokesMethod()
{
// Arrange
var message = SignalRTestHelper.CreateEmptyMessage();
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.NoParams, message);
// Assert
Assert.IsTrue(_service.NoParamsMethodCalled);
SignalRTestHelper.AssertSuccessResponse(_hub.SentMessages[0], TestSignalRTags.NoParams);
}
#endregion
#region Simple Collection Parameter Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_IntArray_DeserializesCorrectly()
{
// Arrange - Arrays are complex objects (not ValueType), use CreateComplexObjectMessage
var values = new[] { 1, 2, 3, 4, 5 };
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.IntArrayParam, message);
// Assert
Assert.IsTrue(_service.IntArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedIntArray);
CollectionAssert.AreEqual(values, _service.ReceivedIntArray);
// Verify response (doubled values)
var responseData = SignalRTestHelper.GetResponseData(_hub.SentMessages[0]);
Assert.IsNotNull(responseData);
CollectionAssert.AreEqual(new[] { 2, 4, 6, 8, 10 }, responseData);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_GuidArray_DeserializesCorrectly()
{
// Arrange
var guids = new[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
var message = SignalRTestHelper.CreateComplexObjectMessage(guids);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.GuidArrayParam, message);
// Assert
Assert.IsTrue(_service.GuidArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedGuidArray);
Assert.AreEqual(3, _service.ReceivedGuidArray.Length);
CollectionAssert.AreEqual(guids, _service.ReceivedGuidArray);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_StringList_DeserializesCorrectly()
{
// Arrange
var items = new List { "apple", "banana", "cherry" };
var message = SignalRTestHelper.CreateComplexObjectMessage(items);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.StringListParam, message);
// Assert
Assert.IsTrue(_service.StringListMethodCalled);
Assert.IsNotNull(_service.ReceivedStringList);
CollectionAssert.AreEqual(items, _service.ReceivedStringList);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_IntList_DeserializesCorrectly()
{
// Arrange
var numbers = new List { 10, 20, 30 };
var message = SignalRTestHelper.CreateComplexObjectMessage(numbers);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.IntListParam, message);
// Assert
Assert.IsTrue(_service.IntListMethodCalled);
Assert.IsNotNull(_service.ReceivedIntList);
CollectionAssert.AreEqual(numbers, _service.ReceivedIntList);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_BoolArray_DeserializesCorrectly()
{
// Arrange
var flags = new[] { true, false, true, true };
var message = SignalRTestHelper.CreateComplexObjectMessage(flags);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.BoolArrayParam, message);
// Assert
Assert.IsTrue(_service.BoolArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedBoolArray);
CollectionAssert.AreEqual(flags, _service.ReceivedBoolArray);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_EmptyIntArray_DeserializesCorrectly()
{
// Arrange
var values = Array.Empty();
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.IntArrayParam, message);
// Assert
Assert.IsTrue(_service.IntArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedIntArray);
Assert.AreEqual(0, _service.ReceivedIntArray.Length);
}
#endregion
#region Complex Collection Parameter Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_TestOrderItemList_DeserializesCorrectly()
{
// Arrange - using shared TestOrderItem list
var items = new List
{
new() { Id = 1, ProductName = "First", Quantity = 1 },
new() { Id = 2, ProductName = "Second", Quantity = 2 },
new() { Id = 3, ProductName = "Third", Quantity = 3 }
};
var message = SignalRTestHelper.CreateComplexObjectMessage(items);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.TestOrderItemListParam, message);
// Assert
Assert.IsTrue(_service.TestOrderItemListMethodCalled);
Assert.AreEqual(3, _service.ReceivedTestOrderItemList?.Count);
Assert.AreEqual("First", _service.ReceivedTestOrderItemList?[0].ProductName);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_NestedList_DeserializesCorrectly()
{
// Arrange
var nestedList = new List>
{
new() { 1, 2, 3 },
new() { 4, 5 },
new() { 6, 7, 8, 9 }
};
var message = SignalRTestHelper.CreateComplexObjectMessage(nestedList);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.NestedListParam, message);
// Assert
Assert.IsTrue(_service.NestedListMethodCalled);
Assert.IsNotNull(_service.ReceivedNestedList);
Assert.AreEqual(3, _service.ReceivedNestedList.Count);
CollectionAssert.AreEqual(new List { 1, 2, 3 }, _service.ReceivedNestedList[0]);
}
#endregion
#region Mixed Parameter Tests (Primitive + Complex)
[TestMethod]
public async Task ProcessOnReceiveMessage_IntAndDto_DeserializesCorrectly()
{
// Arrange
var item = new TestOrderItem { Id = 10, ProductName = "Test", Quantity = 5 };
var message = SignalRTestHelper.CreatePrimitiveParamsMessage(42, item);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.IntAndDtoParam, message);
// Assert
Assert.IsTrue(_service.IntAndDtoMethodCalled);
Assert.IsNotNull(_service.ReceivedIntAndDto);
Assert.AreEqual(42, _service.ReceivedIntAndDto.Value.Item1);
Assert.AreEqual("Test", _service.ReceivedIntAndDto.Value.Item2?.ProductName);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_DtoAndList_DeserializesCorrectly()
{
// Arrange
var item = new TestOrderItem { Id = 1, ProductName = "Test", Quantity = 1 };
var numbers = new List { 1, 2, 3 };
var message = SignalRTestHelper.CreatePrimitiveParamsMessage(item, numbers);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DtoAndListParam, message);
// Assert
Assert.IsTrue(_service.DtoAndListMethodCalled);
Assert.IsNotNull(_service.ReceivedDtoAndList);
Assert.AreEqual("Test", _service.ReceivedDtoAndList.Value.Item1?.ProductName);
CollectionAssert.AreEqual(numbers, _service.ReceivedDtoAndList.Value.Item2);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_ThreeComplexParams_DeserializesCorrectly()
{
// Arrange
var item = new TestOrderItem { Id = 1, ProductName = "Product", Quantity = 1 };
var tags = new List { "tag1", "tag2" };
var sharedTag = new SharedTag { Id = 1, Name = "Shared" };
var message = SignalRTestHelper.CreatePrimitiveParamsMessage(item, tags, sharedTag);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.ThreeComplexParams, message);
// Assert
Assert.IsTrue(_service.ThreeComplexParamsMethodCalled);
Assert.IsNotNull(_service.ReceivedThreeComplexParams);
Assert.AreEqual("Product", _service.ReceivedThreeComplexParams.Value.Item1?.ProductName);
Assert.AreEqual(2, _service.ReceivedThreeComplexParams.Value.Item2?.Count);
Assert.AreEqual("Shared", _service.ReceivedThreeComplexParams.Value.Item3?.Name);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_MixedWithArray_DeserializesCorrectly()
{
// Arrange - Multiple params: bool, int[], string
// When ParamCount > 1, uses IdMessage format regardless of types
var flag = true;
var numbers = new[] { 1, 2, 3 };
var text = "hello";
var message = SignalRTestHelper.CreatePrimitiveParamsMessage(flag, numbers, text);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.MixedWithArrayParam, message);
// Assert
Assert.IsTrue(_service.MixedWithArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedMixedWithArray);
Assert.AreEqual(true, _service.ReceivedMixedWithArray.Value.Item1);
CollectionAssert.AreEqual(numbers, _service.ReceivedMixedWithArray.Value.Item2);
Assert.AreEqual("hello", _service.ReceivedMixedWithArray.Value.Item3);
}
#endregion
#region Error Handling Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_UnknownTag_InvokesNotFoundCallback()
{
// Arrange
const int unknownTag = 9999;
var message = SignalRTestHelper.CreateEmptyMessage();
// Act
await _hub.InvokeProcessOnReceiveMessage(unknownTag, message);
// Assert
Assert.IsTrue(_hub.WasNotFoundCallbackInvoked);
Assert.IsNotNull(_hub.NotFoundTagName);
// Should still send error response
Assert.AreEqual(1, _hub.SentMessages.Count);
SignalRTestHelper.AssertErrorResponse(_hub.SentMessages[0], unknownTag);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_MethodThrows_ReturnsErrorResponse()
{
// Arrange
var message = SignalRTestHelper.CreateEmptyMessage();
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.ThrowsException, message);
// Assert
Assert.AreEqual(1, _hub.SentMessages.Count);
SignalRTestHelper.AssertErrorResponse(_hub.SentMessages[0], TestSignalRTags.ThrowsException);
Assert.IsTrue(_hub.Logger.HasErrorLogs, "Should have logged error");
}
[TestMethod]
public async Task ProcessOnReceiveMessage_EmptyMessage_LogsWarning()
{
// Arrange
byte[] emptyMessage = [];
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.NoParams, emptyMessage);
// Assert
Assert.IsTrue(_hub.Logger.HasWarningLogs, "Should have warning for empty message");
}
#endregion
#region Response Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_Success_SendsToCaller()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(42);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.SingleIntParam, message);
// Assert
Assert.AreEqual(1, _hub.SentMessages.Count);
Assert.AreEqual(SendTarget.Caller, _hub.SentMessages[0].Target);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_WithRequestId_IncludesRequestIdInResponse()
{
// Arrange
var message = SignalRTestHelper.CreateSinglePrimitiveMessage(42);
const int requestId = 12345;
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.SingleIntParam, message, requestId);
// Assert
Assert.AreEqual(requestId, _hub.SentMessages[0].RequestId);
}
#endregion
#region Empty/Null Message Edge Cases
[TestMethod]
public async Task ProcessOnReceiveMessage_EmptyBytes_WithNoParams_Succeeds()
{
// Arrange - empty byte array for method with no parameters
byte[] emptyMessage = [];
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.NoParams, emptyMessage);
// Assert - should succeed because method has no params
Assert.IsTrue(_service.NoParamsMethodCalled);
Assert.IsTrue(_hub.Logger.HasWarningLogs, "Should have warning for empty message");
SignalRTestHelper.AssertSuccessResponse(_hub.SentMessages[0], TestSignalRTags.NoParams);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_EmptyBytes_WithIntParam_ReturnsError()
{
// Arrange - empty byte array for method expecting int parameter
byte[] emptyMessage = [];
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.SingleIntParam, emptyMessage);
// Assert - should return error response, not crash
Assert.IsFalse(_service.SingleIntMethodCalled, "Method should not be called with empty message");
Assert.AreEqual(1, _hub.SentMessages.Count);
SignalRTestHelper.AssertErrorResponse(_hub.SentMessages[0], TestSignalRTags.SingleIntParam);
Assert.IsTrue(_hub.Logger.HasErrorLogs || _hub.Logger.HasWarningLogs);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_EmptyBytes_WithComplexParam_ReturnsError()
{
// Arrange - empty byte array for method expecting TestOrderItem parameter
byte[] emptyMessage = [];
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.TestOrderItemParam, emptyMessage);
// Assert - should return error response, not crash
Assert.IsFalse(_service.TestOrderItemMethodCalled, "Method should not be called with empty message");
Assert.AreEqual(1, _hub.SentMessages.Count);
SignalRTestHelper.AssertErrorResponse(_hub.SentMessages[0], TestSignalRTags.TestOrderItemParam);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_NullMessage_WithNoParams_Succeeds()
{
// Arrange - null message for method with no parameters
byte[]? nullMessage = null;
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.NoParams, nullMessage);
// Assert - should succeed because method has no params
Assert.IsTrue(_service.NoParamsMethodCalled);
SignalRTestHelper.AssertSuccessResponse(_hub.SentMessages[0], TestSignalRTags.NoParams);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_NullMessage_WithIntParam_ReturnsError()
{
// Arrange - null message for method expecting int parameter
byte[]? nullMessage = null;
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.SingleIntParam, nullMessage);
// Assert - should return error response, not crash
Assert.IsFalse(_service.SingleIntMethodCalled, "Method should not be called with null message");
Assert.AreEqual(1, _hub.SentMessages.Count);
SignalRTestHelper.AssertErrorResponse(_hub.SentMessages[0], TestSignalRTags.SingleIntParam);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_EmptyBytes_WithMultipleParams_ReturnsError()
{
// Arrange - empty byte array for method expecting multiple parameters
byte[] emptyMessage = [];
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.TwoIntParams, emptyMessage);
// Assert - should return error response, not crash
Assert.IsFalse(_service.TwoIntMethodCalled, "Method should not be called with empty message");
Assert.AreEqual(1, _hub.SentMessages.Count);
SignalRTestHelper.AssertErrorResponse(_hub.SentMessages[0], TestSignalRTags.TwoIntParams);
}
#endregion
#region Extended Array Parameter Deserialization Tests
[TestMethod]
public async Task ProcessOnReceiveMessage_LongArray_DeserializesCorrectly()
{
// Arrange
var values = new[] { 1L, 9223372036854775807L, -9223372036854775808L, 0L };
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.LongArrayParam, message);
// Assert
Assert.IsTrue(_service.LongArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedLongArray);
Assert.AreEqual(4, _service.ReceivedLongArray.Length);
Assert.AreEqual(9223372036854775807L, _service.ReceivedLongArray[1]);
Assert.AreEqual(-9223372036854775808L, _service.ReceivedLongArray[2]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_DecimalArray_DeserializesCorrectly()
{
// Arrange
var values = new[] { 0.01m, 99.99m, 123456.789m, -999.99m };
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DecimalArrayParam, message);
// Assert
Assert.IsTrue(_service.DecimalArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedDecimalArray);
Assert.AreEqual(4, _service.ReceivedDecimalArray.Length);
Assert.AreEqual(0.01m, _service.ReceivedDecimalArray[0]);
Assert.AreEqual(99.99m, _service.ReceivedDecimalArray[1]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_DateTimeArray_DeserializesCorrectly()
{
// Arrange
var values = new[]
{
new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc),
new DateTime(2024, 12, 31, 23, 59, 59, DateTimeKind.Utc),
DateTime.MinValue,
new DateTime(2000, 6, 15, 12, 30, 45, DateTimeKind.Utc)
};
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DateTimeArrayParam, message);
// Assert
Assert.IsTrue(_service.DateTimeArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedDateTimeArray);
Assert.AreEqual(4, _service.ReceivedDateTimeArray.Length);
Assert.AreEqual(2024, _service.ReceivedDateTimeArray[0].Year);
Assert.AreEqual(12, _service.ReceivedDateTimeArray[1].Month);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_EnumArray_DeserializesCorrectly()
{
// Arrange - using shared TestStatus enum
var values = new[] { TestStatus.Pending, TestStatus.Active, TestStatus.Processing, TestStatus.Shipped };
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.EnumArrayParam, message);
// Assert
Assert.IsTrue(_service.EnumArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedEnumArray);
Assert.AreEqual(4, _service.ReceivedEnumArray.Length);
Assert.AreEqual(TestStatus.Pending, _service.ReceivedEnumArray[0]);
Assert.AreEqual(TestStatus.Shipped, _service.ReceivedEnumArray[3]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_DoubleArray_DeserializesCorrectly()
{
// Arrange
var values = new[] { 3.14159265359, -273.15, 0.0, double.MaxValue, double.MinValue };
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DoubleArrayParam, message);
// Assert
Assert.IsTrue(_service.DoubleArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedDoubleArray);
Assert.AreEqual(5, _service.ReceivedDoubleArray.Length);
Assert.IsTrue(Math.Abs(_service.ReceivedDoubleArray[0] - 3.14159265359) < 0.0000001);
Assert.AreEqual(-273.15, _service.ReceivedDoubleArray[1]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_SharedTagArray_ComplexObjectArray_DeserializesCorrectly()
{
// Arrange - array of IId complex objects
var tags = new[]
{
new SharedTag { Id = 1, Name = "Tag1", Color = "#FF0000", Priority = 1 },
new SharedTag { Id = 2, Name = "Tag2", Color = "#00FF00", Priority = 2 },
new SharedTag { Id = 3, Name = "Tag3", Color = "#0000FF", Priority = 3 }
};
var message = SignalRTestHelper.CreateComplexObjectMessage(tags);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.SharedTagArrayParam, message);
// Assert
Assert.IsTrue(_service.SharedTagArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedSharedTagArray);
Assert.AreEqual(3, _service.ReceivedSharedTagArray.Length);
Assert.AreEqual("Tag1", _service.ReceivedSharedTagArray[0].Name);
Assert.AreEqual("#00FF00", _service.ReceivedSharedTagArray[1].Color);
Assert.AreEqual(3, _service.ReceivedSharedTagArray[2].Priority);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_Dictionary_DeserializesCorrectly()
{
// Arrange
var dict = new Dictionary
{
{ "apple", 1 },
{ "banana", 2 },
{ "cherry", 3 }
};
var message = SignalRTestHelper.CreateComplexObjectMessage(dict);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.DictionaryParam, message);
// Assert
Assert.IsTrue(_service.DictionaryMethodCalled);
Assert.IsNotNull(_service.ReceivedDictionary);
Assert.AreEqual(3, _service.ReceivedDictionary.Count);
Assert.AreEqual(1, _service.ReceivedDictionary["apple"]);
Assert.AreEqual(2, _service.ReceivedDictionary["banana"]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_EmptyGuidArray_DeserializesCorrectly()
{
// Arrange - edge case: empty Guid array
var guids = Array.Empty();
var message = SignalRTestHelper.CreateComplexObjectMessage(guids);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.GuidArrayParam, message);
// Assert
Assert.IsTrue(_service.GuidArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedGuidArray);
Assert.AreEqual(0, _service.ReceivedGuidArray.Length);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_SingleElementArray_DeserializesCorrectly()
{
// Arrange - edge case: single element array
var values = new[] { 42 };
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.IntArrayParam, message);
// Assert
Assert.IsTrue(_service.IntArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedIntArray);
Assert.AreEqual(1, _service.ReceivedIntArray.Length);
Assert.AreEqual(42, _service.ReceivedIntArray[0]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_LargeArray_DeserializesCorrectly()
{
// Arrange - performance edge case: large array
var values = Enumerable.Range(1, 1000).ToArray();
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.IntArrayParam, message);
// Assert
Assert.IsTrue(_service.IntArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedIntArray);
Assert.AreEqual(1000, _service.ReceivedIntArray.Length);
Assert.AreEqual(1, _service.ReceivedIntArray[0]);
Assert.AreEqual(1000, _service.ReceivedIntArray[999]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_ComplexObjectListWithNestedCollections_DeserializesCorrectly()
{
// Arrange - TestOrder contains nested Items list
var orders = new List
{
new() { Id = 1, ProductName = "Product A", Quantity = 10, UnitPrice = 9.99m, Status = TestStatus.Active },
new() { Id = 2, ProductName = "Product B", Quantity = 5, UnitPrice = 19.99m, Status = TestStatus.Processing },
new() { Id = 3, ProductName = "Product C", Quantity = 1, UnitPrice = 99.99m, Status = TestStatus.Shipped }
};
var message = SignalRTestHelper.CreateComplexObjectMessage(orders);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.TestOrderItemListParam, message);
// Assert
Assert.IsTrue(_service.TestOrderItemListMethodCalled);
Assert.IsNotNull(_service.ReceivedTestOrderItemList);
Assert.AreEqual(3, _service.ReceivedTestOrderItemList.Count);
Assert.AreEqual("Product A", _service.ReceivedTestOrderItemList[0].ProductName);
Assert.AreEqual(19.99m, _service.ReceivedTestOrderItemList[1].UnitPrice);
Assert.AreEqual(TestStatus.Shipped, _service.ReceivedTestOrderItemList[2].Status);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_StringArrayWithSpecialChars_DeserializesCorrectly()
{
// Arrange - strings with special characters
var values = new List
{
"normal",
"with spaces",
"with\"quotes\"",
"with\nnewline",
"with\ttab",
"unicode: αινσϊ",
"" // empty string
};
var message = SignalRTestHelper.CreateComplexObjectMessage(values);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.StringListParam, message);
// Assert
Assert.IsTrue(_service.StringListMethodCalled);
Assert.IsNotNull(_service.ReceivedStringList);
Assert.AreEqual(7, _service.ReceivedStringList.Count);
Assert.AreEqual("normal", _service.ReceivedStringList[0]);
Assert.AreEqual("with spaces", _service.ReceivedStringList[1]);
Assert.AreEqual("unicode: αινσϊ", _service.ReceivedStringList[5]);
Assert.AreEqual("", _service.ReceivedStringList[6]);
}
[TestMethod]
public async Task ProcessOnReceiveMessage_GuidArrayWithEmptyGuid_DeserializesCorrectly()
{
// Arrange - includes Guid.Empty
var guids = new[] { Guid.NewGuid(), Guid.Empty, Guid.NewGuid() };
var message = SignalRTestHelper.CreateComplexObjectMessage(guids);
// Act
await _hub.InvokeProcessOnReceiveMessage(TestSignalRTags.GuidArrayParam, message);
// Assert
Assert.IsTrue(_service.GuidArrayMethodCalled);
Assert.IsNotNull(_service.ReceivedGuidArray);
Assert.AreEqual(3, _service.ReceivedGuidArray.Length);
Assert.AreEqual(Guid.Empty, _service.ReceivedGuidArray[1]);
}
[TestMethod]
public void CreateComplexObjectMessage_Dictionary_ProducesCorrectJson()
{
// Arrange
var dict = new Dictionary
{
{ "apple", 1 },
{ "banana", 2 },
{ "cherry", 3 }
};
// Act
var message = SignalRTestHelper.CreateComplexObjectMessage(dict);
var deserialized = MessagePackSerializer.Deserialize>(
message, MessagePackSerializerOptions.Standard);
// Assert
Assert.IsNotNull(deserialized.PostDataJson, "PostDataJson should not be null");
Assert.IsTrue(deserialized.PostDataJson.Contains("apple"),
$"PostDataJson should contain 'apple'. Actual: {deserialized.PostDataJson}");
Assert.IsTrue(deserialized.PostDataJson.StartsWith("{"),
$"PostDataJson should start with {{. Actual first char: {deserialized.PostDataJson[0]}");
}
#endregion
}