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 }