using System.Linq; using AcDream.Core.Allegiance; using Xunit; namespace AcDream.Core.Tests.Allegiance; public sealed class AllegianceTreeTests { [Fact] public void SetMonarch_RegistersNode_FiresEvent() { var tree = new AllegianceTree(); int changes = 0; tree.TreeChanged += () => changes++; tree.SetMonarch(0xAA, "Bael'Zharon"); Assert.Equal(1, changes); Assert.Equal(0xAAu, tree.MonarchGuid); Assert.Equal(1, tree.NodeCount); } [Fact] public void UpsertNode_NewPatron_AddsToVassalList() { var tree = new AllegianceTree(); tree.SetMonarch(0xAA, "Monarch"); tree.UpsertNode(0xBB, "Vassal", patronGuid: 0xAA, rank: 1); var monarch = tree.Get(0xAA); Assert.NotNull(monarch); Assert.Contains(0xBBu, monarch!.VassalGuids); } [Fact] public void UpsertNode_RankClamp_CapsAt10() { var tree = new AllegianceTree(); tree.SetMonarch(0xAA, "M"); var node = tree.UpsertNode(0xBB, "V", 0xAA, rank: 50); Assert.Equal(10, node.Rank); } [Fact] public void RemoveNode_RemovesFromParentVassalList() { var tree = new AllegianceTree(); tree.SetMonarch(0xAA, "M"); tree.UpsertNode(0xBB, "V", 0xAA, 1); Assert.Single(tree.Get(0xAA)!.VassalGuids); tree.RemoveNode(0xBB); Assert.Empty(tree.Get(0xAA)!.VassalGuids); } [Fact] public void GetAncestors_WalksToMonarch() { var tree = new AllegianceTree(); tree.SetMonarch(0xAA, "M"); tree.UpsertNode(0xBB, "V", 0xAA, 1); tree.UpsertNode(0xCC, "VV", 0xBB, 2); tree.UpsertNode(0xDD, "VVV", 0xCC, 3); var chain = tree.GetAncestors(0xDD).Select(n => n.Name).ToArray(); Assert.Equal(new[] { "VVV", "VV", "V", "M" }, chain); } [Fact] public void GetAncestors_HandlesCycleWithoutLooping() { // Corrupt data: defensive path. Treat a cycle as a stop. var tree = new AllegianceTree(); tree.UpsertNode(0xAA, "A", patronGuid: 0xBB, rank: 1); tree.UpsertNode(0xBB, "B", patronGuid: 0xAA, rank: 1); // cycle var walk = tree.GetAncestors(0xAA).Select(n => n.Guid).ToArray(); Assert.Equal(2, walk.Length); // visits each exactly once } [Fact] public void GetDescendants_BfsOrder() { var tree = new AllegianceTree(); tree.SetMonarch(0xAA, "M"); tree.UpsertNode(0xB0, "B0", 0xAA, 1); tree.UpsertNode(0xB1, "B1", 0xAA, 1); tree.UpsertNode(0xC0, "C0", 0xB0, 2); var bfs = tree.GetDescendants(0xAA).Select(n => n.Name).ToArray(); Assert.Equal(new[] { "M", "B0", "B1", "C0" }, bfs); } [Fact] public void AllegianceMath_Passup_KnownCase() { // 1000 XP, loyalty 10, 100 real-time days, 100 in-game days. // loyaltyScale = (50 + 225) / 291 = 0.9450 // ageScale = 1 + (100/730)*(100/720) = 1 + 0.0190 = 1.0190 // passup = 1000 * 0.9450 * 1.0190 ≈ 963 long passup = AllegianceMath.ComputePassup(1000, 10, 100, 100); Assert.InRange(passup, 960, 965); } [Fact] public void AllegianceMath_Passup_ZeroClamp() { // Negative XP shouldn't be possible; passup is clamped at 0. Assert.Equal(0, AllegianceMath.ComputePassup(-1000, 10, 0, 0)); } }