AyCode.Core/AyCode.Core.Tests/Serialization/AcExpressionNodeSerializati...

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
}