1142 lines
40 KiB
C#
1142 lines
40 KiB
C#
using AyCode.Core.Extensions;
|
|
using AyCode.Core.Tests.TestModels;
|
|
using AyCode.Services.Server.SignalRs;
|
|
using AyCode.Services.SignalRs;
|
|
using MessagePack.Resolvers;
|
|
|
|
namespace AyCode.Services.Server.Tests.SignalRs;
|
|
|
|
/// <summary>
|
|
/// Base class for SignalR client-to-hub communication tests.
|
|
/// Tests the full round-trip: Client -> Server -> Service -> Response -> Client
|
|
/// Derived classes specify the serializer type (JSON or Binary).
|
|
/// </summary>
|
|
public abstract class SignalRClientToHubTestBase
|
|
{
|
|
protected abstract AcSerializerOptions SerializerOption { get; }
|
|
|
|
protected TestLogger _logger = null!;
|
|
protected TestableSignalRClient2 _client = null!;
|
|
protected TestableSignalRHub2 _hub = null!;
|
|
protected TestSignalRService2 _service = null!;
|
|
|
|
[TestInitialize]
|
|
public void Setup()
|
|
{
|
|
_logger = new TestLogger();
|
|
_hub = new TestableSignalRHub2();
|
|
_service = new TestSignalRService2();
|
|
_client = new TestableSignalRClient2(_hub, _logger);
|
|
|
|
_hub.SetSerializerType(SerializerOption);
|
|
_hub.RegisterService(_service, _client);
|
|
}
|
|
|
|
#region Primitive Parameter Tests
|
|
|
|
[TestMethod]
|
|
[DataRow(42)]
|
|
[DataRow(0)]
|
|
[DataRow(-100)]
|
|
[DataRow(int.MaxValue)]
|
|
public async Task Post_SingleInt_ReturnsStringRepresentation(int value)
|
|
{
|
|
var result = await _client.PostDataAsync<int, string>(TestSignalRTags.SingleIntParam, value);
|
|
Assert.AreEqual(value.ToString(), result);
|
|
}
|
|
|
|
[TestMethod]
|
|
[DataRow(10, 20, 30)]
|
|
[DataRow(0, 0, 0)]
|
|
[DataRow(-5, 10, 5)]
|
|
public async Task Post_TwoInts_ReturnsSum(int a, int b, int expectedSum)
|
|
{
|
|
var result = await _client.PostAsync<int>(TestSignalRTags.TwoIntParams, [a, b]);
|
|
Assert.AreEqual(expectedSum, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
[DataRow(true)]
|
|
[DataRow(false)]
|
|
public async Task Post_Bool_ReturnsSameValue(bool value)
|
|
{
|
|
var result = await _client.PostDataAsync<bool, bool>(TestSignalRTags.BoolParam, value);
|
|
Assert.AreEqual(value, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
[DataRow("Hello")]
|
|
[DataRow("")]
|
|
[DataRow("Special chars: <>\"'&")]
|
|
public async Task Post_String_ReturnsEcho(string text)
|
|
{
|
|
var result = await _client.PostDataAsync<string, string>(TestSignalRTags.StringParam, text);
|
|
Assert.AreEqual($"Echo: {text}", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_Guid_ReturnsSameValue()
|
|
{
|
|
var guid = Guid.NewGuid();
|
|
var result = await _client.PostDataAsync<Guid, Guid>(TestSignalRTags.GuidParam, guid);
|
|
Assert.AreEqual(guid, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_GuidEmpty_ReturnsSameValue()
|
|
{
|
|
var result = await _client.PostDataAsync<Guid, Guid>(TestSignalRTags.GuidParam, Guid.Empty);
|
|
Assert.AreEqual(Guid.Empty, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
[DataRow(TestStatus.Pending, TestStatus.Pending)]
|
|
[DataRow(TestStatus.Processing, TestStatus.Processing)]
|
|
[DataRow(TestStatus.Completed, TestStatus.Completed)]
|
|
public async Task Post_Enum_ReturnsSameValue(TestStatus input, TestStatus expected)
|
|
{
|
|
var result = await _client.PostDataAsync<TestStatus, TestStatus>(TestSignalRTags.EnumParam, input);
|
|
Assert.AreEqual(expected, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Get_NoParams_ReturnsOK()
|
|
{
|
|
var result = await _client.GetAllAsync<string>(TestSignalRTags.NoParams);
|
|
Assert.AreEqual("OK", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_MultipleTypes_ReturnsConcatenated()
|
|
{
|
|
var result = await _client.PostAsync<string>(TestSignalRTags.MultipleTypesParams, [true, "test", 42]);
|
|
Assert.AreEqual("True-test-42", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
[DataRow(123.45)]
|
|
[DataRow(0.0)]
|
|
[DataRow(-99.99)]
|
|
public async Task Post_Decimal_ReturnsDoubled(double inputDouble)
|
|
{
|
|
var input = (decimal)inputDouble;
|
|
var result = await _client.PostDataAsync<decimal, decimal>(TestSignalRTags.DecimalParam, input);
|
|
Assert.AreEqual(input * 2, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_DateTime_ReturnsSameValue()
|
|
{
|
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 45, DateTimeKind.Utc);
|
|
var result = await _client.PostDataAsync<DateTime, DateTime>(TestSignalRTags.DateTimeParam, dateTime);
|
|
Assert.AreEqual(dateTime, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
[DataRow(3.14159)]
|
|
[DataRow(0.0)]
|
|
[DataRow(-1.5)]
|
|
public async Task Post_Double_ReturnsSameValue(double value)
|
|
{
|
|
var result = await _client.PostDataAsync<double, double>(TestSignalRTags.DoubleParam, value);
|
|
Assert.AreEqual(value, result, 0.0001);
|
|
}
|
|
|
|
[TestMethod]
|
|
[DataRow(9223372036854775807L)]
|
|
[DataRow(0L)]
|
|
[DataRow(-1L)]
|
|
public async Task Post_Long_ReturnsSameValue(long value)
|
|
{
|
|
var result = await _client.PostDataAsync<long, long>(TestSignalRTags.LongParam, value);
|
|
Assert.AreEqual(value, result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Complex Object Tests
|
|
|
|
[TestMethod]
|
|
public async Task Post_TestOrderItem_ReturnsProcessedItem()
|
|
{
|
|
var item = new TestOrderItem { Id = 1, ProductName = "Widget", Quantity = 5, UnitPrice = 10.50m };
|
|
|
|
var result = await _client.PostDataAsync(TestSignalRTags.TestOrderItemParam, item);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(1, result.Id);
|
|
Assert.AreEqual("Processed: Widget", result.ProductName);
|
|
Assert.AreEqual(item.Quantity * 2, result.Quantity);
|
|
Assert.AreEqual(item.UnitPrice * 2, result.UnitPrice);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_TestOrder_ReturnsSameOrder()
|
|
{
|
|
var order = TestDataFactory.CreateOrder(itemCount: 2);
|
|
|
|
var result = await _client.PostDataAsync<TestOrder, TestOrder>(TestSignalRTags.TestOrderParam, order);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(order.Id, result.Id);
|
|
Assert.AreEqual(order.OrderNumber, result.OrderNumber);
|
|
Assert.AreEqual(2, result.Items.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_SharedTag_ReturnsSameTag()
|
|
{
|
|
var tag = new SharedTag { Id = 1, Name = "Important", Color = "#FF0000" };
|
|
|
|
var result = await _client.PostDataAsync<SharedTag, SharedTag>(TestSignalRTags.SharedTagParam, tag);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(1, result.Id);
|
|
Assert.AreEqual("Important", result.Name);
|
|
Assert.AreEqual("#FF0000", result.Color);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Collection Parameter Tests
|
|
|
|
[TestMethod]
|
|
public async Task Post_IntArray_ReturnsDoubledValues()
|
|
{
|
|
var input = new[] { 1, 2, 3, 4, 5 };
|
|
|
|
var result = await _client.PostDataAsync<int[], int[]>(TestSignalRTags.IntArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(new[] { 2, 4, 6, 8, 10 }, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_GuidArray_ReturnsSameValues()
|
|
{
|
|
var guids = new[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
|
|
|
|
var result = await _client.PostDataAsync<Guid[], Guid[]>(TestSignalRTags.GuidArrayParam, guids);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(guids, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_StringList_ReturnsUppercased()
|
|
{
|
|
var input = new List<string> { "apple", "banana", "cherry" };
|
|
|
|
var result = await _client.PostDataAsync<List<string>, List<string>>(TestSignalRTags.StringListParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(new List<string> { "APPLE", "BANANA", "CHERRY" }, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_TestOrderItemList_ReturnsSameItems()
|
|
{
|
|
var items = new List<TestOrderItem>
|
|
{
|
|
new() { Id = 1, ProductName = "Item1" },
|
|
new() { Id = 2, ProductName = "Item2" }
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<List<TestOrderItem>, List<TestOrderItem>>(TestSignalRTags.TestOrderItemListParam, items);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(2, result.Count);
|
|
Assert.AreEqual("Item1", result[0].ProductName);
|
|
Assert.AreEqual("Item2", result[1].ProductName);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_IntList_ReturnsDoubledValues()
|
|
{
|
|
var input = new List<int> { 10, 20, 30 };
|
|
|
|
var result = await _client.PostDataAsync<List<int>, List<int>>(TestSignalRTags.IntListParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(new List<int> { 20, 40, 60 }, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_BoolArray_ReturnsSameValues()
|
|
{
|
|
var input = new[] { true, false, true, false };
|
|
|
|
var result = await _client.PostDataAsync<bool[], bool[]>(TestSignalRTags.BoolArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(input, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_MixedWithArray_ReturnsFormattedString()
|
|
{
|
|
var numbers = new[] { 1, 2, 3 };
|
|
|
|
var result = await _client.PostAsync<string>(TestSignalRTags.MixedWithArrayParam, [true, numbers, "end"]);
|
|
|
|
Assert.AreEqual("True-[1,2,3]-end", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_NestedList_ReturnsSameStructure()
|
|
{
|
|
var nested = new List<List<int>>
|
|
{
|
|
new() { 1, 2, 3 },
|
|
new() { 4, 5 },
|
|
new() { 6 }
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<List<List<int>>, List<List<int>>>(TestSignalRTags.NestedListParam, nested);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(3, result.Count);
|
|
CollectionAssert.AreEqual(new List<int> { 1, 2, 3 }, result[0]);
|
|
CollectionAssert.AreEqual(new List<int> { 4, 5 }, result[1]);
|
|
CollectionAssert.AreEqual(new List<int> { 6 }, result[2]);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Extended Array Tests
|
|
|
|
[TestMethod]
|
|
public async Task Post_LongArray_ReturnsSameValues()
|
|
{
|
|
var input = new[] { 1L, 2L, long.MaxValue };
|
|
|
|
var result = await _client.PostDataAsync<long[], long[]>(TestSignalRTags.LongArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(input, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task GetAll_WithEmptyArrayContextParam_ReturnsResult()
|
|
{
|
|
var result = await _client.GetAllAsync<int[]>(TestSignalRTags.IntArrayParam, [Array.Empty<int>()]);
|
|
|
|
Assert.IsNotNull(result, "Result should not be null");
|
|
Assert.AreEqual(0, result.Length, "Empty array should return empty array");
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task GetAll_WithArrayContextParam_ReturnsDoubledValues()
|
|
{
|
|
var input = new[] { 1, 2, 3 };
|
|
var result = await _client.GetAllAsync<int[]>(TestSignalRTags.IntArrayParam, [input]);
|
|
|
|
Assert.IsNotNull(result, "Result should not be null");
|
|
CollectionAssert.AreEqual(new[] { 2, 4, 6 }, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_DecimalArray_ReturnsSameValues()
|
|
{
|
|
var input = new[] { 1.1m, 2.2m, 3.3m };
|
|
|
|
var result = await _client.PostDataAsync<decimal[], decimal[]>(TestSignalRTags.DecimalArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(input, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_DateTimeArray_ReturnsSameValues()
|
|
{
|
|
var input = new[]
|
|
{
|
|
new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
|
new DateTime(2024, 6, 15, 12, 30, 0, DateTimeKind.Utc)
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<DateTime[], DateTime[]>(TestSignalRTags.DateTimeArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(2, result.Length);
|
|
Assert.AreEqual(input[0], result[0]);
|
|
Assert.AreEqual(input[1], result[1]);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_EnumArray_ReturnsSameValues()
|
|
{
|
|
var input = new[] { TestStatus.Pending, TestStatus.Completed, TestStatus.Processing };
|
|
|
|
var result = await _client.PostDataAsync<TestStatus[], TestStatus[]>(TestSignalRTags.EnumArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
CollectionAssert.AreEqual(input, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_DoubleArray_ReturnsSameValues()
|
|
{
|
|
var input = new[] { 1.1, 2.2, 3.3 };
|
|
|
|
var result = await _client.PostDataAsync<double[], double[]>(TestSignalRTags.DoubleArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(3, result.Length);
|
|
Assert.AreEqual(1.1, result[0], 0.001);
|
|
Assert.AreEqual(2.2, result[1], 0.001);
|
|
Assert.AreEqual(3.3, result[2], 0.001);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_SharedTagArray_ReturnsSameValues()
|
|
{
|
|
var input = new[]
|
|
{
|
|
new SharedTag { Id = 1, Name = "Tag1", Color = "#FF0000" },
|
|
new SharedTag { Id = 2, Name = "Tag2", Color = "#00FF00" }
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<SharedTag[], SharedTag[]>(TestSignalRTags.SharedTagArrayParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(2, result.Length);
|
|
Assert.AreEqual("Tag1", result[0].Name);
|
|
Assert.AreEqual("Tag2", result[1].Name);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_Dictionary_ReturnsSameValues()
|
|
{
|
|
var input = new Dictionary<string, int>
|
|
{
|
|
{ "one", 1 },
|
|
{ "two", 2 },
|
|
{ "three", 3 }
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<Dictionary<string, int>, Dictionary<string, int>>(TestSignalRTags.DictionaryParam, input);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(3, result.Count);
|
|
Assert.AreEqual(1, result["one"]);
|
|
Assert.AreEqual(2, result["two"]);
|
|
Assert.AreEqual(3, result["three"]);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Mixed Parameter Tests
|
|
|
|
[TestMethod]
|
|
public async Task Post_IntAndDto_ReturnsFormattedString()
|
|
{
|
|
var item = new TestOrderItem { Id = 1, ProductName = "Widget" };
|
|
|
|
var result = await _client.PostAsync<string>(TestSignalRTags.IntAndDtoParam, [42, item]);
|
|
|
|
Assert.AreEqual("42-Widget", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_DtoAndList_ReturnsFormattedString()
|
|
{
|
|
var item = new TestOrderItem { Id = 1, ProductName = "Product" };
|
|
var numbers = new List<int> { 1, 2, 3 };
|
|
|
|
var result = await _client.PostAsync<string>(TestSignalRTags.DtoAndListParam, [item, numbers]);
|
|
|
|
Assert.AreEqual("Product-[1,2,3]", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_ThreeComplexParams_ReturnsFormattedString()
|
|
{
|
|
var item = new TestOrderItem { Id = 1, ProductName = "Item" };
|
|
var tags = new List<string> { "tag1", "tag2", "tag3" };
|
|
var sharedTag = new SharedTag { Id = 1, Name = "Shared" };
|
|
|
|
var result = await _client.PostAsync<string>(TestSignalRTags.ThreeComplexParams, [item, tags, sharedTag]);
|
|
|
|
Assert.AreEqual("Item-3-Shared", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Post_FiveParams_ReturnsFormattedString()
|
|
{
|
|
var guid = Guid.Parse("12345678-1234-1234-1234-123456789abc");
|
|
|
|
var result = await _client.PostAsync<string>(TestSignalRTags.FiveParams, [42, "hello", true, guid, 99.99m]);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual($"42-hello-True-{guid}-99.99", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task GetByIdAsync_FiveParams_ReturnsFormattedString()
|
|
{
|
|
var guid = Guid.NewGuid();
|
|
|
|
var result = await _client.GetByIdAsync<string>(TestSignalRTags.FiveParams, [1, "text", true, guid, 99.99m]);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual($"1-text-True-{guid}-99.99", result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Error Handling Tests
|
|
|
|
[TestMethod]
|
|
public async Task Post_ThrowsException_ReturnsError()
|
|
{
|
|
var result = await _client.GetAllAsync<string>(TestSignalRTags.ThrowsException);
|
|
|
|
Assert.IsNull(result);
|
|
Assert.IsTrue(_logger.HasErrorLogs);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Async Task<T> Method Tests
|
|
|
|
[TestMethod]
|
|
public async Task Async_TestOrderItem_ReturnsProcessedItem()
|
|
{
|
|
var item = new TestOrderItem { Id = 1, ProductName = "Widget", Quantity = 5, UnitPrice = 10.50m };
|
|
|
|
var result = await _client.PostDataAsync(TestSignalRTags.AsyncTestOrderItemParam, item);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(1, result.Id);
|
|
Assert.AreEqual("Async: Widget", result.ProductName);
|
|
Assert.AreEqual(item.Quantity * 3, result.Quantity);
|
|
Assert.AreEqual(item.UnitPrice * 3, result.UnitPrice);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Async_String_ReturnsProcessedString()
|
|
{
|
|
var result = await _client.PostDataAsync<string, string>(TestSignalRTags.AsyncStringParam, "TestInput");
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual("Async: TestInput", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Async_NoParams_ReturnsAsyncOK()
|
|
{
|
|
var result = await _client.GetAllAsync<string>(TestSignalRTags.AsyncNoParams);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual("AsyncOK", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Async_Int_ReturnsDoubledValue()
|
|
{
|
|
var result = await _client.PostDataAsync<int, int>(TestSignalRTags.AsyncIntParam, 42);
|
|
|
|
Assert.AreEqual(84, result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Round-Trip Integrity Tests
|
|
|
|
[TestMethod]
|
|
public async Task RoundTrip_ComplexObject_PreservesAllProperties()
|
|
{
|
|
var item = new TestOrderItem
|
|
{
|
|
Id = 999,
|
|
ProductName = "RoundTrip Test Item",
|
|
Quantity = 50,
|
|
UnitPrice = 123.45m,
|
|
Status = TestStatus.Processing
|
|
};
|
|
|
|
var result = await _client.PostDataAsync(TestSignalRTags.TestOrderItemParam, item);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(999, result.Id);
|
|
Assert.AreEqual("Processed: RoundTrip Test Item", result.ProductName);
|
|
Assert.AreEqual(100, result.Quantity);
|
|
Assert.AreEqual(246.90m, result.UnitPrice);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task RoundTrip_NestedOrder_PreservesHierarchy()
|
|
{
|
|
var order = TestDataFactory.CreateOrder(itemCount: 3, palletsPerItem: 2, measurementsPerPallet: 1);
|
|
|
|
var result = await _client.PostDataAsync<TestOrder, TestOrder>(TestSignalRTags.TestOrderParam, order);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(order.Id, result.Id);
|
|
Assert.AreEqual(order.OrderNumber, result.OrderNumber);
|
|
Assert.AreEqual(3, result.Items.Count);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
Assert.AreEqual(order.Items[i].Id, result.Items[i].Id);
|
|
Assert.AreEqual(2, result.Items[i].Pallets.Count);
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task RoundTrip_SpecialCharacters_PreservedCorrectly()
|
|
{
|
|
var testString = "Special: \"quotes\" 'apostrophes' <brackets> & ampersand \\ backslash \n newline";
|
|
|
|
var result = await _client.PostDataAsync<string, string>(TestSignalRTags.StringParam, testString);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual($"Echo: {testString}", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task RoundTrip_UnicodeCharacters_PreservedCorrectly()
|
|
{
|
|
var testString = "Unicode: 中文 日本語 한국어 🎉 émoji";
|
|
|
|
var result = await _client.PostDataAsync<string, string>(TestSignalRTags.StringParam, testString);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual($"Echo: {testString}", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task RoundTrip_EmptyString_PreservedCorrectly()
|
|
{
|
|
var result = await _client.PostDataAsync<string, string>(TestSignalRTags.StringParam, "");
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual("Echo: ", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task RoundTrip_LargeDecimal_PreservedCorrectly()
|
|
{
|
|
var largeDecimal = 999999999999.999999m;
|
|
|
|
var result = await _client.PostDataAsync<decimal, decimal>(TestSignalRTags.DecimalParam, largeDecimal);
|
|
|
|
Assert.AreEqual(largeDecimal * 2, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task RoundTrip_ExtremeInt_PreservedCorrectly()
|
|
{
|
|
var result = await _client.PostDataAsync<int, string>(TestSignalRTags.SingleIntParam, int.MaxValue);
|
|
|
|
Assert.AreEqual(int.MaxValue.ToString(), result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Response Data Integrity Tests
|
|
|
|
[TestMethod]
|
|
public async Task ResponseData_NotDoubleEscaped()
|
|
{
|
|
var item = new TestOrderItem { Id = 1, ProductName = "Test", Quantity = 10, UnitPrice = 20m };
|
|
|
|
var result = await _client.PostDataAsync(TestSignalRTags.TestOrderItemParam, item);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(1, result.Id);
|
|
Assert.AreEqual("Processed: Test", result.ProductName);
|
|
Assert.AreEqual(20, result.Quantity);
|
|
Assert.AreEqual(40m, result.UnitPrice);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task CollectionResponse_DeserializesCorrectly()
|
|
{
|
|
var items = new List<TestOrderItem>
|
|
{
|
|
new() { Id = 1, ProductName = "Item1", Quantity = 10, UnitPrice = 1.1m },
|
|
new() { Id = 2, ProductName = "Item2", Quantity = 20, UnitPrice = 2.2m },
|
|
new() { Id = 3, ProductName = "Item3", Quantity = 30, UnitPrice = 3.3m }
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<List<TestOrderItem>, List<TestOrderItem>>(
|
|
TestSignalRTags.TestOrderItemListParam, items);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(3, result.Count);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
Assert.AreEqual(items[i].Id, result[i].Id);
|
|
Assert.AreEqual(items[i].ProductName, result[i].ProductName);
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Async_Method_ReturnsActualResult_NotTaskWrapper()
|
|
{
|
|
var item = new TestOrderItem { Id = 42, ProductName = "TestProduct", Quantity = 5, UnitPrice = 10m };
|
|
|
|
var result = await _client.PostDataAsync(TestSignalRTags.AsyncTestOrderItemParam, item);
|
|
|
|
Assert.IsNotNull(result, "Result should not be null");
|
|
Assert.AreEqual(42, result.Id, "Id should match the original");
|
|
Assert.AreEqual("Async: TestProduct", result.ProductName,
|
|
"ProductName should be 'Async: TestProduct', not null.");
|
|
Assert.AreEqual(15, result.Quantity, "Quantity should be tripled (5*3=15).");
|
|
Assert.AreEqual(30m, result.UnitPrice, "UnitPrice should be tripled (10*3=30).");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Task.FromResult Integration Tests
|
|
|
|
[TestMethod]
|
|
public async Task TaskFromResult_String_ReturnsActualResult_NotTaskWrapper()
|
|
{
|
|
var result = await _client.PostDataAsync<string, string>(TestSignalRTags.TaskFromResultStringParam, "TestInput");
|
|
|
|
Assert.IsNotNull(result, "Result should not be null");
|
|
Assert.AreEqual("FromResult: TestInput", result,
|
|
"Should return the actual string, not Task wrapper JSON");
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task TaskFromResult_ComplexObject_ReturnsActualResult_NotTaskWrapper()
|
|
{
|
|
var item = new TestOrderItem { Id = 42, ProductName = "TestProduct", Quantity = 5, UnitPrice = 10m };
|
|
|
|
var result = await _client.PostDataAsync(TestSignalRTags.TaskFromResultTestOrderItemParam, item);
|
|
|
|
Assert.IsNotNull(result, "Result should not be null");
|
|
Assert.AreEqual(42, result.Id, "Id should match the original");
|
|
Assert.AreEqual("FromResult: TestProduct", result.ProductName,
|
|
"ProductName should be 'FromResult: TestProduct'.");
|
|
Assert.AreEqual(10, result.Quantity, "Quantity should be doubled (5*2=10).");
|
|
Assert.AreEqual(20m, result.UnitPrice, "UnitPrice should be doubled (10*2=20).");
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task TaskFromResult_Int_ReturnsActualResult_NotTaskWrapper()
|
|
{
|
|
var result = await _client.PostDataAsync<int, int>(TestSignalRTags.TaskFromResultIntParam, 42);
|
|
|
|
Assert.AreEqual(84, result, "Should return doubled value (42*2=84).");
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task TaskFromResult_NoResult_CompletesSuccessfully()
|
|
{
|
|
var result = await _client.GetAllAsync<object>(TestSignalRTags.TaskFromResultNoParams);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region InvokeMethod Unit Tests
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_SyncMethod_ReturnsValue()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleSingleInt")!;
|
|
var result = methodInfo.InvokeMethod(service, 42);
|
|
|
|
Assert.AreEqual("42", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_AsyncTaskTMethod_ReturnsUnwrappedValue()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleAsyncString")!;
|
|
|
|
var result = methodInfo.InvokeMethod(service, "Test");
|
|
|
|
Assert.IsNotNull(result, "InvokeMethod should unwrap Task<T> and return the result");
|
|
Assert.AreEqual("Async: Test", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_AsyncTaskTMethod_WithComplexObject_ReturnsUnwrappedValue()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleAsyncTestOrderItem")!;
|
|
var input = new TestOrderItem { Id = 1, ProductName = "Widget", Quantity = 5, UnitPrice = 10m };
|
|
|
|
var result = methodInfo.InvokeMethod(service, input);
|
|
|
|
Assert.IsNotNull(result, "InvokeMethod should unwrap Task<TestOrderItem> and return the result");
|
|
Assert.IsInstanceOfType(result, typeof(TestOrderItem));
|
|
var item = (TestOrderItem)result;
|
|
Assert.AreEqual("Async: Widget", item.ProductName);
|
|
Assert.AreEqual(15, item.Quantity);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_AsyncTaskTMethod_WithInt_ReturnsUnwrappedValue()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleAsyncInt")!;
|
|
|
|
var result = methodInfo.InvokeMethod(service, 42);
|
|
|
|
Assert.IsNotNull(result, "InvokeMethod should unwrap Task<int> and return the result");
|
|
Assert.AreEqual(84, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_TaskFromResult_ReturnsUnwrappedValue()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleTaskFromResultString")!;
|
|
|
|
var result = methodInfo.InvokeMethod(service, "Test");
|
|
|
|
Assert.IsNotNull(result, "InvokeMethod should unwrap Task.FromResult<T> and return the result");
|
|
Assert.AreEqual("FromResult: Test", result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_TaskFromResult_WithComplexObject_ReturnsUnwrappedValue()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleTaskFromResultTestOrderItem")!;
|
|
var input = new TestOrderItem { Id = 1, ProductName = "Widget", Quantity = 5, UnitPrice = 10m };
|
|
|
|
var result = methodInfo.InvokeMethod(service, input);
|
|
|
|
Assert.IsNotNull(result, "InvokeMethod should unwrap Task.FromResult<TestOrderItem> and return the result");
|
|
Assert.IsInstanceOfType(result, typeof(TestOrderItem));
|
|
var item = (TestOrderItem)result;
|
|
Assert.AreEqual("FromResult: Widget", item.ProductName);
|
|
Assert.AreEqual(10, item.Quantity);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_TaskFromResult_WithInt_ReturnsUnwrappedValue()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleTaskFromResultInt")!;
|
|
|
|
var result = methodInfo.InvokeMethod(service, 42);
|
|
|
|
Assert.IsNotNull(result, "InvokeMethod should unwrap Task.FromResult<int> and return the result");
|
|
Assert.AreEqual(84, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void InvokeMethod_NonGenericTask_CompletesWithoutError()
|
|
{
|
|
var service = new TestSignalRService2();
|
|
var methodInfo = typeof(TestSignalRService2).GetMethod("HandleTaskFromResultNoParams")!;
|
|
|
|
var result = methodInfo.InvokeMethod(service);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GenericAttributes Tests
|
|
|
|
[TestMethod]
|
|
public async Task GenericAttributes_WithDateTimeString_PreservesExactValue()
|
|
{
|
|
var dto = new TestDtoWithGenericAttributes
|
|
{
|
|
Id = 123,
|
|
Name = "Test Order",
|
|
GenericAttributes =
|
|
[
|
|
new TestGenericAttribute { Id = 1, Key = "DateOfReceipt", Value = "10/24/2025 00:27:00" },
|
|
new TestGenericAttribute { Id = 2, Key = "Priority", Value = "1" },
|
|
new TestGenericAttribute { Id = 3, Key = "SomeFlag", Value = "true" }
|
|
]
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<TestDtoWithGenericAttributes, TestDtoWithGenericAttributes>(
|
|
TestSignalRTags.GenericAttributesParam, dto);
|
|
|
|
Assert.IsNotNull(result, "Result should not be null");
|
|
Assert.AreEqual(123, result.Id);
|
|
Assert.AreEqual("Test Order", result.Name);
|
|
Assert.AreEqual(3, result.GenericAttributes.Count);
|
|
|
|
var dateAttr = result.GenericAttributes.FirstOrDefault(x => x.Key == "DateOfReceipt");
|
|
Assert.IsNotNull(dateAttr, "DateOfReceipt attribute should exist");
|
|
Assert.AreEqual("10/24/2025 00:27:00", dateAttr.Value);
|
|
|
|
var priorityAttr = result.GenericAttributes.FirstOrDefault(x => x.Key == "Priority");
|
|
Assert.IsNotNull(priorityAttr);
|
|
Assert.AreEqual("1", priorityAttr.Value);
|
|
|
|
var flagAttr = result.GenericAttributes.FirstOrDefault(x => x.Key == "SomeFlag");
|
|
Assert.IsNotNull(flagAttr);
|
|
Assert.AreEqual("true", flagAttr.Value);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task GenericAttributes_EmptyList_PreservesEmptyList()
|
|
{
|
|
var dto = new TestDtoWithGenericAttributes
|
|
{
|
|
Id = 789,
|
|
Name = "Empty Attributes",
|
|
GenericAttributes = []
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<TestDtoWithGenericAttributes, TestDtoWithGenericAttributes>(
|
|
TestSignalRTags.GenericAttributesParam, dto);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(789, result.Id);
|
|
Assert.AreEqual("Empty Attributes", result.Name);
|
|
Assert.IsNotNull(result.GenericAttributes);
|
|
Assert.AreEqual(0, result.GenericAttributes.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task GenericAttributes_ManyItems_AllPreserved()
|
|
{
|
|
var dto = new TestDtoWithGenericAttributes
|
|
{
|
|
Id = 999,
|
|
Name = "Many Attributes",
|
|
GenericAttributes = Enumerable.Range(0, 50).Select(i => new TestGenericAttribute
|
|
{
|
|
Id = i,
|
|
Key = $"Attribute_{i}",
|
|
Value = i % 5 == 0 ? $"{i}/24/2025 00:00:00" : i.ToString()
|
|
}).ToList()
|
|
};
|
|
|
|
var result = await _client.PostDataAsync<TestDtoWithGenericAttributes, TestDtoWithGenericAttributes>(
|
|
TestSignalRTags.GenericAttributesParam, dto);
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(50, result.GenericAttributes.Count);
|
|
|
|
for (int i = 0; i < 50; i++)
|
|
{
|
|
var expectedValue = i % 5 == 0 ? $"{i}/24/2025 00:00:00" : i.ToString();
|
|
Assert.AreEqual($"Attribute_{i}", result.GenericAttributes[i].Key);
|
|
Assert.AreEqual(expectedValue, result.GenericAttributes[i].Value);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Property Mismatch Tests (Server has more properties than Client - tests SkipValue)
|
|
|
|
/// <summary>
|
|
/// REGRESSION TEST: Tests the case where server sends a DTO with more properties than the client knows about.
|
|
/// Bug: "Invalid interned string index: 15. Interned strings count: 12"
|
|
/// Root cause: When deserializing, unknown properties are skipped via SkipValue(), but the skipped
|
|
/// string values were not being registered in the intern table, causing index mismatch for later StringInterned references.
|
|
///
|
|
/// This test simulates the production bug where CustomerDto had properties on server
|
|
/// that the client didn't have defined.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task PropertyMismatch_ServerHasMoreProperties_DeserializesCorrectly()
|
|
{
|
|
// Arrange: Create "server" DTO with many properties
|
|
var serverDto = new ServerCustomerDto
|
|
{
|
|
Id = 1,
|
|
FirstName = "John",
|
|
LastName = "Smith",
|
|
Email = "john.smith@example.com",
|
|
Phone = "+1-555-1234",
|
|
Address = "123 Main Street",
|
|
City = "New York",
|
|
Country = "USA",
|
|
PostalCode = "10001",
|
|
Company = "Acme Corp",
|
|
Department = "Engineering",
|
|
Notes = "VIP customer with special requirements",
|
|
Status = TestStatus.Active,
|
|
IsVerified = true,
|
|
LoginCount = 42,
|
|
Balance = 1234.56m
|
|
};
|
|
|
|
// Act: Send server DTO, receive client DTO (fewer properties)
|
|
// This simulates the real bug scenario
|
|
var result = await _client.PostDataAsync<ServerCustomerDto, ClientCustomerDto>(
|
|
TestSignalRTags.PropertyMismatchParam, serverDto);
|
|
|
|
// Assert: Client should receive only the properties it knows about
|
|
Assert.IsNotNull(result, "Result should not be null - deserialization should succeed even with unknown properties");
|
|
Assert.AreEqual(1, result.Id);
|
|
Assert.AreEqual("John", result.FirstName);
|
|
Assert.AreEqual("Smith", result.LastName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// REGRESSION TEST: Tests a list of DTOs with property mismatch.
|
|
/// This more closely simulates the production bug with GetMeasuringUsers returning List<CustomerDto>.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task PropertyMismatch_ListOfDtos_WithManyProperties_DeserializesCorrectly()
|
|
{
|
|
// Arrange: Create list of "server" DTOs with many string properties
|
|
var serverDtos = Enumerable.Range(0, 25).Select(i => new ServerCustomerDto
|
|
{
|
|
Id = i,
|
|
FirstName = $"FirstName_{i % 10}", // 10 unique values (will be interned)
|
|
LastName = $"LastName_{i % 8}", // 8 unique values
|
|
Email = $"user{i}@example.com",
|
|
Phone = $"+1-555-{i:D4}",
|
|
Address = $"Address_{i % 5}", // 5 unique values
|
|
City = i % 3 == 0 ? "New York" : i % 3 == 1 ? "Los Angeles" : "Chicago",
|
|
Country = "USA",
|
|
PostalCode = $"{10000 + i}",
|
|
Company = $"Company_{i % 6}", // 6 unique values
|
|
Department = i % 4 == 0 ? "Engineering" : i % 4 == 1 ? "Sales" : i % 4 == 2 ? "Marketing" : "Support",
|
|
Notes = $"Notes for customer {i}",
|
|
Status = (TestStatus)(i % 5),
|
|
IsVerified = i % 2 == 0,
|
|
LoginCount = i * 10,
|
|
Balance = i * 100.50m
|
|
}).ToList();
|
|
|
|
// Act: Send list of server DTOs, receive list of client DTOs
|
|
var result = await _client.PostDataAsync<List<ServerCustomerDto>, List<ClientCustomerDto>>(
|
|
TestSignalRTags.PropertyMismatchListParam, serverDtos);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(result, "Result should not be null");
|
|
Assert.AreEqual(serverDtos.Count, result.Count, $"Expected {serverDtos.Count} items");
|
|
|
|
for (int i = 0; i < serverDtos.Count; i++)
|
|
{
|
|
Assert.AreEqual(serverDtos[i].Id, result[i].Id, $"Id mismatch at index {i}");
|
|
Assert.AreEqual(serverDtos[i].FirstName, result[i].FirstName,
|
|
$"FirstName mismatch at index {i}: expected '{serverDtos[i].FirstName}', got '{result[i].FirstName}'");
|
|
Assert.AreEqual(serverDtos[i].LastName, result[i].LastName,
|
|
$"LastName mismatch at index {i}: expected '{serverDtos[i].LastName}', got '{result[i].LastName}'");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// REGRESSION TEST: Tests nested objects being skipped when client doesn't know about them.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task PropertyMismatch_NestedObjectsSkipped_DeserializesCorrectly()
|
|
{
|
|
// Arrange: Server order with nested customer object
|
|
var serverOrder = new ServerOrderWithExtras
|
|
{
|
|
Id = 100,
|
|
OrderNumber = "ORD-2024-001",
|
|
TotalAmount = 999.99m,
|
|
Customer = new ServerCustomerDto
|
|
{
|
|
Id = 1,
|
|
FirstName = "John",
|
|
LastName = "Doe",
|
|
Email = "john@example.com",
|
|
Phone = "+1-555-0001"
|
|
},
|
|
RelatedCustomers =
|
|
[
|
|
new ServerCustomerDto { Id = 2, FirstName = "Jane", LastName = "Smith", Email = "jane@example.com" },
|
|
new ServerCustomerDto { Id = 3, FirstName = "Bob", LastName = "Wilson", Email = "bob@example.com" }
|
|
],
|
|
InternalNotes = "Priority processing required",
|
|
ProcessingCode = "RUSH-001"
|
|
};
|
|
|
|
// Act: Send server order, receive simplified client order
|
|
var result = await _client.PostDataAsync<ServerOrderWithExtras, ClientOrderSimple>(
|
|
TestSignalRTags.PropertyMismatchNestedParam, serverOrder);
|
|
|
|
// Assert: Client should receive only basic order info
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(100, result.Id);
|
|
Assert.AreEqual("ORD-2024-001", result.OrderNumber);
|
|
Assert.AreEqual(999.99m, result.TotalAmount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// REGRESSION TEST: Large list with nested objects being skipped.
|
|
/// This is the most comprehensive test for the SkipValue string interning bug.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task PropertyMismatch_LargeListWithNestedObjects_DeserializesCorrectly()
|
|
{
|
|
// Arrange: Create 50 orders with nested customers
|
|
var serverOrders = Enumerable.Range(0, 50).Select(i => new ServerOrderWithExtras
|
|
{
|
|
Id = i,
|
|
OrderNumber = $"ORD-{i:D4}",
|
|
TotalAmount = i * 100.50m,
|
|
Customer = new ServerCustomerDto
|
|
{
|
|
Id = i * 100,
|
|
FirstName = $"Customer_{i % 10}",
|
|
LastName = $"LastName_{i % 8}",
|
|
Email = $"customer{i}@example.com",
|
|
Company = $"Company_{i % 5}"
|
|
},
|
|
RelatedCustomers = Enumerable.Range(0, i % 3 + 1).Select(j => new ServerCustomerDto
|
|
{
|
|
Id = i * 100 + j,
|
|
FirstName = $"Related_{j}",
|
|
LastName = $"Contact_{i % 4}",
|
|
Email = $"related{i}_{j}@example.com"
|
|
}).ToList(),
|
|
InternalNotes = $"Notes for order {i}",
|
|
ProcessingCode = $"CODE-{i % 10}"
|
|
}).ToList();
|
|
|
|
// Act
|
|
var result = await _client.PostDataAsync<List<ServerOrderWithExtras>, List<ClientOrderSimple>>(
|
|
TestSignalRTags.PropertyMismatchNestedListParam, serverOrders);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(result, "Result should not be null - SkipValue should correctly handle unknown nested objects");
|
|
Assert.AreEqual(serverOrders.Count, result.Count);
|
|
|
|
for (int i = 0; i < serverOrders.Count; i++)
|
|
{
|
|
Assert.AreEqual(serverOrders[i].Id, result[i].Id, $"Id mismatch at index {i}");
|
|
Assert.AreEqual(serverOrders[i].OrderNumber, result[i].OrderNumber,
|
|
$"OrderNumber mismatch at index {i}: expected '{serverOrders[i].OrderNumber}', got '{result[i].OrderNumber}'");
|
|
Assert.AreEqual(serverOrders[i].TotalAmount, result[i].TotalAmount, $"TotalAmount mismatch at index {i}");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs all SignalR tests with JSON serialization.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class SignalRClientToHubTest_Json : SignalRClientToHubTestBase
|
|
{
|
|
protected override AcSerializerOptions SerializerOption { get; } = new AcJsonSerializerOptions();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs all SignalR tests with Binary serialization.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class SignalRClientToHubTest_Binary_WithRef : SignalRClientToHubTestBase
|
|
{
|
|
protected override AcSerializerOptions SerializerOption { get; } = new AcBinarySerializerOptions();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs all SignalR tests with Binary serialization.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class SignalRClientToHubTest_Binary_NoRef : SignalRClientToHubTestBase
|
|
{
|
|
protected override AcSerializerOptions SerializerOption { get; } = new AcBinarySerializerOptions
|
|
{
|
|
UseReferenceHandling = false
|
|
};
|
|
} |