using AyCode.Core.Extensions; using static AyCode.Core.Tests.Serialization.AcSerializerModels; namespace AyCode.Core.Tests.Serialization; /// /// Tests for circular reference handling with back-navigation properties. /// [TestClass] public class AcBinarySerializerCircularReferenceTests { /// /// 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 /// [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() }; var child = new CircularChild { Id = 10, ParentId = 1, Name = "Child", Created = DateTime.UtcNow.AddHours(-1), Modified = DateTime.UtcNow, Parent = parent, GrandChildren = new List() }; 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(); 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"); } /// /// Test list of parents with circular references. /// [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() }; 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() }; 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>(); 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"); } } } }