318 lines
12 KiB
C#
318 lines
12 KiB
C#
using AyCode.Core.Serializers.Binaries;
|
|
using AyCode.Core.Serializers.Expressions;
|
|
using AyCode.Core.Serializers.Jsons;
|
|
using AyCode.Core.Tests.TestModels;
|
|
|
|
namespace AyCode.Core.Tests.Serialization;
|
|
|
|
/// <summary>
|
|
/// Tests for AcExpressionNode serialization.
|
|
/// These tests verify that the serializers properly handle expression nodes
|
|
/// with the new type-safe constant value storage.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class AcExpressionNodeSerializationTests
|
|
{
|
|
#region JSON Serializer Tests
|
|
|
|
/// <summary>
|
|
/// Tests that AcExpressionNode with type-safe properties can be JSON serialized.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void AcJsonSerializer_WithAcExpressionNode_RoundTrip_Works()
|
|
{
|
|
// Arrange - Create an expression with a constant value
|
|
System.Linq.Expressions.Expression<Func<TestOrderItem_All_True, bool>> filterExpression =
|
|
item => item.Quantity > 5;
|
|
|
|
var expressionNode = AcExpressionConverter.ToNode(filterExpression);
|
|
|
|
// Act - Serialize
|
|
var json = AcJsonSerializer.Serialize(expressionNode);
|
|
Console.WriteLine($"Serialized JSON: {json}");
|
|
|
|
Assert.IsNotNull(json, "JSON should not be null");
|
|
Assert.IsTrue(json.Length > 0, "JSON should not be empty");
|
|
|
|
// Deserialize
|
|
var deserialized = AcJsonDeserializer.Deserialize<AcExpressionNode>(json);
|
|
Assert.IsNotNull(deserialized, "Deserialized node should not be null");
|
|
|
|
// Rebuild and test
|
|
var rebuiltExpression = AcExpressionRebuilder.FromNode<TestOrderItem_All_True, bool>(deserialized);
|
|
var compiled = rebuiltExpression.Compile();
|
|
|
|
var matchingItem = new TestOrderItem_All_True { Id = 1, Quantity = 10 };
|
|
var nonMatchingItem = new TestOrderItem_All_True { Id = 2, Quantity = 3 };
|
|
|
|
Assert.IsTrue(compiled(matchingItem), "Matching item should pass filter");
|
|
Assert.IsFalse(compiled(nonMatchingItem), "Non-matching item should fail filter");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests that decimal constant values are preserved after JSON round-trip.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void AcJsonSerializer_WithDecimalValue_PreservesType()
|
|
{
|
|
// Arrange - Node with decimal constant
|
|
var node = new AcExpressionNode
|
|
{
|
|
NodeType = System.Linq.Expressions.ExpressionType.Constant,
|
|
TypeName = "System.Decimal"
|
|
};
|
|
node.SetValue(99.99m);
|
|
|
|
// Act - JSON round-trip
|
|
var json = AcJsonSerializer.Serialize(node);
|
|
Console.WriteLine($"JSON: {json}");
|
|
|
|
var deserialized = AcJsonDeserializer.Deserialize<AcExpressionNode>(json);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(deserialized);
|
|
Assert.AreEqual(ConstantValueType.Decimal, deserialized.ValueType);
|
|
|
|
var value = deserialized.GetValue();
|
|
Assert.IsNotNull(value, "Value should not be null");
|
|
Assert.AreEqual(typeof(decimal), value.GetType(), "Should preserve decimal type");
|
|
Assert.AreEqual(99.99m, (decimal)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests that Guid constant values are preserved after JSON round-trip.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void AcJsonSerializer_WithGuidValue_PreservesType()
|
|
{
|
|
// Arrange
|
|
var testGuid = Guid.NewGuid();
|
|
var node = new AcExpressionNode
|
|
{
|
|
NodeType = System.Linq.Expressions.ExpressionType.Constant,
|
|
TypeName = "System.Guid"
|
|
};
|
|
node.SetValue(testGuid);
|
|
|
|
// Act
|
|
var json = AcJsonSerializer.Serialize(node);
|
|
Console.WriteLine($"JSON: {json}");
|
|
|
|
var deserialized = AcJsonDeserializer.Deserialize<AcExpressionNode>(json);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(deserialized);
|
|
Assert.AreEqual(ConstantValueType.Guid, deserialized.ValueType);
|
|
|
|
var value = deserialized.GetValue();
|
|
Assert.IsNotNull(value);
|
|
Assert.AreEqual(typeof(Guid), value.GetType(), "Should preserve Guid type");
|
|
Assert.AreEqual(testGuid, (Guid)value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Binary Serializer Tests
|
|
|
|
/// <summary>
|
|
/// Tests AcBinarySerializer with AcExpressionNode round-trip.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void AcBinarySerializer_WithAcExpressionNode_RoundTrip_Works()
|
|
{
|
|
// Arrange
|
|
System.Linq.Expressions.Expression<Func<TestOrderItem_All_True, bool>> filterExpression =
|
|
item => item.Quantity > 5;
|
|
|
|
var originalNode = AcExpressionConverter.ToNode(filterExpression);
|
|
|
|
// Act - Binary round-trip
|
|
var bytes = AcBinarySerializer.Serialize(originalNode);
|
|
Console.WriteLine($"Binary size: {bytes.Length} bytes");
|
|
|
|
var deserialized = AcBinaryDeserializer.Deserialize<AcExpressionNode>(bytes);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(deserialized);
|
|
Assert.AreEqual(originalNode.NodeType, deserialized.NodeType);
|
|
|
|
// Rebuild and test
|
|
var rebuiltExpression = AcExpressionRebuilder.FromNode<TestOrderItem_All_True, bool>(deserialized);
|
|
var compiled = rebuiltExpression.Compile();
|
|
|
|
var matchingItem = new TestOrderItem_All_True { Id = 1, Quantity = 10 };
|
|
var nonMatchingItem = new TestOrderItem_All_True { Id = 2, Quantity = 3 };
|
|
|
|
Assert.IsTrue(compiled(matchingItem), "Matching item should pass filter");
|
|
Assert.IsFalse(compiled(nonMatchingItem), "Non-matching item should fail filter");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests AcBinarySerializer with decimal in typed property.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void AcBinarySerializer_WithDecimalValue_PreservesType()
|
|
{
|
|
// Arrange
|
|
var node = new AcExpressionNode
|
|
{
|
|
NodeType = System.Linq.Expressions.ExpressionType.Constant,
|
|
TypeName = "System.Decimal"
|
|
};
|
|
node.SetValue(99.99m);
|
|
|
|
// Act
|
|
var bytes = AcBinarySerializer.Serialize(node);
|
|
var deserialized = AcBinaryDeserializer.Deserialize<AcExpressionNode>(bytes);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(deserialized);
|
|
Assert.AreEqual(ConstantValueType.Decimal, deserialized.ValueType);
|
|
|
|
var value = deserialized.GetValue();
|
|
Assert.IsNotNull(value);
|
|
Assert.AreEqual(typeof(decimal), value.GetType(), "Binary should preserve decimal type");
|
|
Assert.AreEqual(99.99m, (decimal)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests complex expression with decimal comparison.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void AcBinarySerializer_WithDecimalComparison_RoundTrip_Works()
|
|
{
|
|
// Arrange - Expression with captured decimal: item => item.UnitPrice > 99.99m
|
|
var minPrice = 99.99m;
|
|
System.Linq.Expressions.Expression<Func<TestOrderItem_All_True, bool>> filterExpression =
|
|
item => item.UnitPrice > minPrice;
|
|
|
|
var originalNode = AcExpressionConverter.ToNode(filterExpression);
|
|
|
|
// Act - Binary round-trip
|
|
var bytes = AcBinarySerializer.Serialize(originalNode);
|
|
var deserializedNode = AcBinaryDeserializer.Deserialize<AcExpressionNode>(bytes);
|
|
|
|
// Assert - Rebuild and verify it still works with decimal comparison
|
|
Assert.IsNotNull(deserializedNode);
|
|
|
|
var rebuiltExpression = AcExpressionRebuilder.FromNode<TestOrderItem_All_True, bool>(deserializedNode);
|
|
var compiledFilter = rebuiltExpression.Compile();
|
|
|
|
var expensiveItem = new TestOrderItem_All_True { UnitPrice = 150m };
|
|
var cheapItem = new TestOrderItem_All_True { UnitPrice = 50m };
|
|
|
|
Assert.IsTrue(compiledFilter(expensiveItem), "Expensive item should pass filter");
|
|
Assert.IsFalse(compiledFilter(cheapItem), "Cheap item should fail filter");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests enum constant values.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void AcBinarySerializer_WithEnumValue_PreservesType()
|
|
{
|
|
// Arrange - Expression with enum comparison
|
|
System.Linq.Expressions.Expression<Func<TestOrderItem_All_True, bool>> filterExpression =
|
|
item => item.Status == TestStatus.Completed;
|
|
|
|
var originalNode = AcExpressionConverter.ToNode(filterExpression);
|
|
|
|
// Act - Binary round-trip
|
|
var bytes = AcBinarySerializer.Serialize(originalNode);
|
|
var deserializedNode = AcBinaryDeserializer.Deserialize<AcExpressionNode>(bytes);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(deserializedNode);
|
|
|
|
var rebuiltExpression = AcExpressionRebuilder.FromNode<TestOrderItem_All_True, bool>(deserializedNode);
|
|
var compiledFilter = rebuiltExpression.Compile();
|
|
|
|
var completedItem = new TestOrderItem_All_True { Status = TestStatus.Completed };
|
|
var pendingItem = new TestOrderItem_All_True { Status = TestStatus.Pending };
|
|
|
|
Assert.IsTrue(compiledFilter(completedItem), "Completed item should pass filter");
|
|
Assert.IsFalse(compiledFilter(pendingItem), "Pending item should fail filter");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetValue/GetDynamicValue Tests
|
|
|
|
[TestMethod]
|
|
[DataRow(42, ConstantValueType.Int32)]
|
|
[DataRow(long.MaxValue, ConstantValueType.Int64)]
|
|
[DataRow(3.14, ConstantValueType.Double)]
|
|
[DataRow(true, ConstantValueType.Boolean)]
|
|
[DataRow("test", ConstantValueType.String)]
|
|
public void SetValue_GetValue_RoundTrip_PreservesValue(object value, ConstantValueType expectedType)
|
|
{
|
|
// Arrange
|
|
var node = new AcExpressionNode();
|
|
|
|
// Act
|
|
node.SetValue(value);
|
|
var result = node.GetValue();
|
|
|
|
// Assert
|
|
Assert.AreEqual(expectedType, node.ValueType);
|
|
Assert.AreEqual(value, result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SetValue_Null_SetsNullType()
|
|
{
|
|
var node = new AcExpressionNode();
|
|
node.SetValue(null);
|
|
|
|
Assert.AreEqual(ConstantValueType.Null, node.ValueType);
|
|
Assert.IsNull(node.GetValue());
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SetValue_Guid_PreservesValue()
|
|
{
|
|
var guid = Guid.NewGuid();
|
|
var node = new AcExpressionNode();
|
|
node.SetValue(guid);
|
|
|
|
Assert.AreEqual(ConstantValueType.Guid, node.ValueType);
|
|
Assert.AreEqual(guid, node.GetValue());
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SetValue_DateTime_PreservesValue()
|
|
{
|
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 45, DateTimeKind.Utc);
|
|
var node = new AcExpressionNode();
|
|
node.SetValue(dateTime);
|
|
|
|
Assert.AreEqual(ConstantValueType.DateTime, node.ValueType);
|
|
Assert.AreEqual(dateTime, node.GetValue());
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SetValue_Decimal_PreservesValue()
|
|
{
|
|
var value = 123.456789m;
|
|
var node = new AcExpressionNode();
|
|
node.SetValue(value);
|
|
|
|
Assert.AreEqual(ConstantValueType.Decimal, node.ValueType);
|
|
Assert.AreEqual(value, node.GetValue());
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SetValue_Enum_PreservesValue()
|
|
{
|
|
var node = new AcExpressionNode();
|
|
node.SetValue(TestStatus.Processing);
|
|
|
|
Assert.AreEqual(ConstantValueType.Enum, node.ValueType);
|
|
Assert.IsTrue(node.EnumTypeName!.StartsWith("AyCode.Core.Tests.TestModels.TestStatus"),
|
|
$"EnumTypeName should start with full type name, got: {node.EnumTypeName}");
|
|
Assert.AreEqual(TestStatus.Processing, node.GetValue());
|
|
}
|
|
|
|
#endregion
|
|
}
|