using AyCode.Core.Extensions;
using MessagePack;
using MessagePack.Resolvers;
namespace AyCode.Core.Tests.TestModels;
///
/// Common SignalR test/benchmark infrastructure.
/// Provides message creation and serialization helpers used by both tests and benchmarks.
///
public static class SignalRMessageFactory
{
///
/// Cached MessagePack options for ContractlessStandardResolver
///
public static readonly MessagePackSerializerOptions ContractlessOptions = ContractlessStandardResolver.Options;
///
/// Creates a MessagePack message for multiple parameters using IdMessage format.
/// Each parameter is serialized directly as JSON.
///
public static byte[] CreateIdMessage(params object[] values)
{
var idMessage = new SignalRIdMessageDto(values);
var postMessage = new SignalRPostMessageDto { PostDataJson = idMessage.ToJson() };
return MessagePackSerializer.Serialize(postMessage, ContractlessOptions);
}
///
/// Creates a MessagePack message for a single primitive parameter.
///
public static byte[] CreateSingleParamMessage(T value) where T : notnull
{
return CreateIdMessage(value);
}
///
/// Creates a MessagePack message for a complex object parameter.
/// Uses PostDataJson pattern for single complex objects.
///
public static byte[] CreateComplexObjectMessage(T obj)
{
var json = obj.ToJson();
var postMessage = new SignalRPostMessageDto { PostDataJson = json };
return MessagePackSerializer.Serialize(postMessage, ContractlessOptions);
}
///
/// Creates an empty MessagePack message for parameterless methods.
///
public static byte[] CreateEmptyMessage()
{
var postMessage = new SignalRPostMessageDto();
return MessagePackSerializer.Serialize(postMessage, ContractlessOptions);
}
///
/// Creates a response message in MessagePack format.
///
public static byte[] CreateResponseMessage(int messageTag, byte status, string? responseDataJson)
{
var response = new SignalRResponseDto
{
MessageTag = messageTag,
Status = status,
ResponseData = responseDataJson
};
return MessagePackSerializer.Serialize(response, ContractlessOptions);
}
///
/// Creates a success response message in MessagePack format.
///
public static byte[] CreateSuccessResponse(int messageTag, T data)
{
return CreateResponseMessage(messageTag, 5, data.ToJson()); // 5 = Success
}
///
/// Creates an error response message in MessagePack format.
///
public static byte[] CreateErrorResponse(int messageTag)
{
return CreateResponseMessage(messageTag, 0, null); // 0 = Error
}
///
/// Deserializes a MessagePack message to IdMessage DTO.
///
public static SignalRIdMessageDto? DeserializeToIdMessage(byte[] messageBytes)
{
if (messageBytes == null || messageBytes.Length == 0) return null;
try
{
var postMessage = MessagePackSerializer.Deserialize(messageBytes, ContractlessOptions);
return postMessage.PostDataJson?.JsonTo();
}
catch
{
return null;
}
}
///
/// Deserializes a MessagePack response message.
///
public static SignalRResponseDto? DeserializeResponse(byte[] messageBytes)
{
if (messageBytes == null || messageBytes.Length == 0) return null;
try
{
return MessagePackSerializer.Deserialize(messageBytes, ContractlessOptions);
}
catch
{
return null;
}
}
}
///
/// Lightweight DTO for IdMessage serialization/deserialization in tests and benchmarks.
/// Mirrors the structure of IdMessage without dependencies on AyCode.Services.
///
public class SignalRIdMessageDto
{
public List Ids { get; set; } = [];
public SignalRIdMessageDto()
{
}
public SignalRIdMessageDto(object[] ids)
{
Ids.AddRange(ids.Select(x => x.ToJson()));
}
public SignalRIdMessageDto(object id)
{
Ids.Add(id.ToJson());
}
}
///
/// Lightweight DTO for SignalR post message serialization.
/// Mirrors SignalPostJsonMessage structure.
///
[MessagePackObject]
public class SignalRPostMessageDto
{
[Key(0)]
public string? PostDataJson { get; set; }
}
///
/// Lightweight DTO for SignalR response message serialization.
/// Mirrors SignalResponseJsonMessage structure.
///
[MessagePackObject]
public class SignalRResponseDto
{
[Key(0)]
public int MessageTag { get; set; }
[Key(1)]
public byte Status { get; set; }
[Key(2)]
public string? ResponseData { get; set; }
[IgnoreMember]
public bool IsSuccess => Status == 5;
[IgnoreMember]
public bool IsError => Status == 0;
}
///
/// Common SignalR message tags for testing.
/// These mirror the production tags but are defined here for test/benchmark independence.
///
public static class CommonSignalRTags
{
// Primitive parameter tags
public const int SingleIntParam = 100;
public const int TwoIntParams = 101;
public const int BoolParam = 102;
public const int StringParam = 103;
public const int GuidParam = 104;
public const int EnumParam = 105;
public const int NoParams = 107;
public const int MultipleTypesParams = 109;
// Extended primitives
public const int DecimalParam = 140;
public const int DateTimeParam = 141;
public const int DoubleParam = 143;
public const int LongParam = 144;
// Complex object tags
public const int TestOrderItemParam = 120;
public const int TestOrderParam = 121;
public const int SharedTagParam = 122;
// Collection tags
public const int IntArrayParam = 130;
public const int GuidArrayParam = 131;
public const int StringListParam = 132;
public const int TestOrderItemListParam = 133;
public const int IntListParam = 134;
public const int BoolArrayParam = 135;
public const int MixedWithArrayParam = 136;
// Mixed parameter scenarios
public const int IntAndDtoParam = 160;
public const int DtoAndListParam = 161;
public const int ThreeComplexParams = 162;
public const int FiveParams = 164;
}
///
/// Pre-built test messages for benchmarking.
/// Caches serialized messages to avoid setup overhead in benchmark iterations.
///
public class SignalRBenchmarkData
{
// Pre-serialized messages
public byte[] SingleIntMessage { get; }
public byte[] TwoIntMessage { get; }
public byte[] FiveParamsMessage { get; }
public byte[] ComplexOrderItemMessage { get; }
public byte[] ComplexOrderMessage { get; }
public byte[] IntArrayMessage { get; }
public byte[] MixedParamsMessage { get; }
// Test data
public TestOrderItem TestOrderItem { get; }
public TestOrder TestOrder { get; }
public int[] IntArray { get; }
public Guid TestGuid { get; }
public SignalRBenchmarkData()
{
// Create test data
TestGuid = Guid.NewGuid();
IntArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
TestOrderItem = new TestOrderItem
{
Id = 42,
ProductName = "Benchmark Product",
Quantity = 100,
UnitPrice = 99.99m,
Status = TestStatus.Active
};
TestOrder = TestDataFactory.CreateOrder(itemCount: 3, palletsPerItem: 2, measurementsPerPallet: 2);
// Pre-serialize messages
SingleIntMessage = SignalRMessageFactory.CreateSingleParamMessage(42);
TwoIntMessage = SignalRMessageFactory.CreateIdMessage(10, 20);
FiveParamsMessage = SignalRMessageFactory.CreateIdMessage(42, "hello", true, TestGuid, 99.99m);
ComplexOrderItemMessage = SignalRMessageFactory.CreateComplexObjectMessage(TestOrderItem);
ComplexOrderMessage = SignalRMessageFactory.CreateComplexObjectMessage(TestOrder);
IntArrayMessage = SignalRMessageFactory.CreateComplexObjectMessage(IntArray);
MixedParamsMessage = SignalRMessageFactory.CreateIdMessage(true, IntArray, "hello");
}
}