161 lines
5.7 KiB
C#
161 lines
5.7 KiB
C#
using AyCode.Core.Extensions;
|
|
using static AyCode.Core.Tests.Serialization.AcSerializerModels;
|
|
|
|
namespace AyCode.Core.Tests.Serialization;
|
|
|
|
/// <summary>
|
|
/// Tests for circular reference handling with back-navigation properties.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class AcBinarySerializerCircularReferenceTests
|
|
{
|
|
/// <summary>
|
|
/// CRITICAL TEST: Circular references with back-navigation properties.
|
|
/// This simulates the exact production scenario where:
|
|
/// - StockTaking has StockTakingItems collection
|
|
/// - StockTakingItem has StockTaking back-reference (circular!)
|
|
/// - StockTakingItem has Product navigation property
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void Deserialize_CircularReference_ParentChildBackReference()
|
|
{
|
|
var parent = new CircularParent
|
|
{
|
|
Id = 1,
|
|
Name = "Parent",
|
|
Created = new DateTime(2025, 1, 24, 15, 25, 0, DateTimeKind.Utc),
|
|
Modified = DateTime.UtcNow,
|
|
Creator = 6,
|
|
Children = new List<CircularChild>()
|
|
};
|
|
|
|
var child = new CircularChild
|
|
{
|
|
Id = 10,
|
|
ParentId = 1,
|
|
Name = "Child",
|
|
Created = DateTime.UtcNow.AddHours(-1),
|
|
Modified = DateTime.UtcNow,
|
|
Parent = parent,
|
|
GrandChildren = new List<CircularGrandChild>()
|
|
};
|
|
|
|
var grandChild = new CircularGrandChild
|
|
{
|
|
Id = 100,
|
|
ChildId = 10,
|
|
CreatorId = 6,
|
|
ModifierId = null,
|
|
Created = DateTime.UtcNow,
|
|
Modified = DateTime.UtcNow,
|
|
Child = child
|
|
};
|
|
|
|
child.GrandChildren.Add(grandChild);
|
|
parent.Children.Add(child);
|
|
|
|
var binary = parent.ToBinary();
|
|
var result = binary.BinaryTo<CircularParent>();
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(1, result.Id);
|
|
Assert.AreEqual(6, result.Creator, "Creator should be 6");
|
|
Assert.AreEqual(parent.Created.Ticks, result.Created.Ticks,
|
|
$"Created mismatch. Expected: {parent.Created}, Got: {result.Created}");
|
|
|
|
Assert.IsNotNull(result.Children);
|
|
Assert.AreEqual(1, result.Children.Count);
|
|
|
|
var resultChild = result.Children[0];
|
|
Assert.AreEqual(10, resultChild.Id);
|
|
Assert.AreEqual(resultChild.Created.Ticks, child.Created.Ticks, "Child.Created should match");
|
|
|
|
Assert.IsNotNull(resultChild.Parent, "Child.Parent back-reference should be resolved");
|
|
Assert.AreEqual(1, resultChild.Parent.Id, "Back-reference should point to same parent");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test list of parents with circular references.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void Deserialize_ListOfCircularReferences_AllItemsCorrect()
|
|
{
|
|
var parents = Enumerable.Range(1, 5).Select(p =>
|
|
{
|
|
var parent = new CircularParent
|
|
{
|
|
Id = p,
|
|
Name = $"Parent_{p}",
|
|
Created = DateTime.UtcNow.AddDays(-p),
|
|
Modified = DateTime.UtcNow,
|
|
Creator = p,
|
|
Children = new List<CircularChild>()
|
|
};
|
|
|
|
for (int c = 1; c <= 2; c++)
|
|
{
|
|
var child = new CircularChild
|
|
{
|
|
Id = p * 100 + c,
|
|
ParentId = p,
|
|
Name = $"Child_{p}_{c}",
|
|
Created = DateTime.UtcNow.AddHours(-c),
|
|
Modified = DateTime.UtcNow,
|
|
Parent = parent,
|
|
GrandChildren = new List<CircularGrandChild>()
|
|
};
|
|
|
|
for (int g = 1; g <= 2; g++)
|
|
{
|
|
child.GrandChildren.Add(new CircularGrandChild
|
|
{
|
|
Id = p * 1000 + c * 100 + g,
|
|
ChildId = child.Id,
|
|
CreatorId = g % 2 == 0 ? p : null,
|
|
ModifierId = g % 2 == 1 ? p * 2 : null,
|
|
Created = DateTime.UtcNow,
|
|
Modified = DateTime.UtcNow,
|
|
Child = child
|
|
});
|
|
}
|
|
|
|
parent.Children.Add(child);
|
|
}
|
|
|
|
return parent;
|
|
}).ToList();
|
|
|
|
var binary = parents.ToBinary();
|
|
var result = binary.BinaryTo<List<CircularParent>>();
|
|
|
|
Assert.IsNotNull(result);
|
|
Assert.AreEqual(5, result.Count);
|
|
|
|
for (int p = 0; p < 5; p++)
|
|
{
|
|
var original = parents[p];
|
|
var deserialized = result[p];
|
|
|
|
Assert.AreEqual(original.Id, deserialized.Id, $"Parent[{p}].Id mismatch");
|
|
Assert.AreEqual(original.Creator, deserialized.Creator, $"Parent[{p}].Creator mismatch");
|
|
Assert.AreEqual(original.Created.Ticks, deserialized.Created.Ticks,
|
|
$"Parent[{p}].Created mismatch. Expected: {original.Created}, Got: {deserialized.Created}");
|
|
|
|
Assert.IsNotNull(deserialized.Children, $"Parent[{p}].Children is null");
|
|
Assert.AreEqual(2, deserialized.Children.Count, $"Parent[{p}].Children.Count mismatch");
|
|
|
|
for (int c = 0; c < 2; c++)
|
|
{
|
|
var origChild = original.Children![c];
|
|
var deserChild = deserialized.Children[c];
|
|
|
|
Assert.AreEqual(origChild.Id, deserChild.Id, $"Parent[{p}].Children[{c}].Id mismatch");
|
|
Assert.AreEqual(origChild.Created.Ticks, deserChild.Created.Ticks,
|
|
$"Parent[{p}].Children[{c}].Created mismatch");
|
|
|
|
Assert.IsNotNull(deserChild.Parent, $"Parent[{p}].Children[{c}].Parent should not be null");
|
|
}
|
|
}
|
|
}
|
|
}
|