using System.Text.Json; using XFEEDToolBox.Core.Navigation; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture] public class SystemOrbitSourceTests { private static readonly DateTimeOffset At=DateTimeOffset.Parse("2026-10-25T10:00:00Z"); private static SystemLocation Body(ulong id,string name,SystemLocationCategory category,string? identity=null) =>new((int)id,id,name,"",0,category,null,new((double)id,0,0),null,false,identity??id.ToString()); private static readonly SystemLocation Star=Body(101,"Fixture A",SystemLocationCategory.Star); private static readonly SystemLocation Planet=Body(102,"Fixture A 1",SystemLocationCategory.Planet); private static CurrentSystemSnapshot Snapshot(params SystemLocation[] bodies)=>new(7,"Fixture",42,1,At,bodies); private static JsonElement Scan(int body,string name,int parent,bool star=false,long system=7,DateTimeOffset? at=null,double a=1e9,double e=.2) =>JsonSerializer.SerializeToElement(new{ @event="Scan",timestamp=at??At,SystemAddress=system,BodyID=body,BodyName=name, StarType=star?"G":"",Parents=new[]{new Dictionary{{"Star",parent}}},SemiMajorAxis=a,Eccentricity=e, OrbitalInclination=20.0,Periapsis=30.0,AscendingNode=40.0,OrbitalPeriod=86400.0,MeanAnomaly=50.0 }); [Test]public void RealJournalElementsBindBySystemAndUniqueOriginalBodyNameWithoutInventingNativeAxes() { var source=new JournalSystemOrbitCatalogue();source.Apply(Scan(1,Star.Name,0,star:true));source.Apply(Scan(2,Planet.Name,1)); var result=source.Bind(Snapshot(Star,Planet)).Single(orbit=>orbit.ChildIdentity==Planet.Identity); Check.Equal(Planet.WorldAddress,result.ChildWorldAddress);Check.Equal(Star.WorldAddress,result.ParentWorldAddress); Check.Equal(2,result.JournalBodyId);Check.Equal(1e9,result.SemiMajorAxisMetres);Check.Equal(.2,result.Eccentricity); Check.Equal(At,result.Epoch);Check.Equal(86400d,result.PeriodSeconds);Check.Equal(50d,result.MeanAnomalyDegrees); Check.Equal(SystemOrbitQuality.ElementsOnly,result.Quality);Check.That(!result.Renderable); Check.That(result.PeriapsisDirection is null&&result.TransverseDirection is null); } [Test]public void CrossSystemOrAmbiguousCatalogueNamesCannotAcquireOrbitIdentity() { var source=new JournalSystemOrbitCatalogue();source.Apply(Scan(2,Planet.Name,1,system:8)); Check.Equal(SystemOrbitQuality.Unavailable,source.Bind(Snapshot(Star,Planet)).Last().Quality); source.Apply(Scan(2,Planet.Name,1)); var duplicate=Body(103,Planet.Name,SystemLocationCategory.Planet); Check.That(source.Bind(Snapshot(Star,Planet,duplicate)).All(orbit=>orbit.JournalBodyId is null&&!orbit.Renderable)); Check.That(source.Bind(Snapshot(Body(104,Planet.Name,SystemLocationCategory.Star))).Single().JournalBodyId is null); } [Test]public void IncompleteParentAndInvalidShapeStayExplicitlyUnavailable() { var source=new JournalSystemOrbitCatalogue();source.Apply(Scan(2,Planet.Name,99)); var unresolved=source.Bind(Snapshot(Planet)).Single();Check.That(unresolved.ParentWorldAddress is null&&!unresolved.Renderable); Check.That(unresolved.UnavailableReason.Contains("父天体")); source.Apply(Scan(2,Planet.Name,99,at:At.AddSeconds(1),e:1)); Check.Equal(SystemOrbitQuality.Unavailable,source.Bind(Snapshot(Planet)).Single().Quality); source.Apply(Scan(2,Planet.Name,99,at:At.AddSeconds(2),a:0)); Check.That(!source.Bind(Snapshot(Planet)).Single().HasEllipticElements); } [Test]public void OlderJournalReplayCannotReplaceNewerOrbitEpochAndSignalsGetNoGuessedCircles() { var source=new JournalSystemOrbitCatalogue();source.Apply(Scan(2,Planet.Name,1,at:At.AddSeconds(5),a:2e9)); source.Apply(Scan(2,Planet.Name,1)); var signal=Body(103,"Signal",SystemLocationCategory.Signal); var result=source.Bind(Snapshot(Planet,signal));Check.Equal(1,result.Count);Check.Equal(2e9,result[0].SemiMajorAxisMetres); Check.Equal(At.AddSeconds(5),result[0].Epoch); } [Test]public void NativeQualityAloneCannotRenderUnknownShapeOrNonOrthonormalAxes() { var valid=new SystemOrbit{SystemAddress=7,ChildWorldAddress=102,ChildIdentity="child",ParentWorldAddress=101,ParentIdentity="parent", SemiMajorAxisMetres=1e9,Eccentricity=.2,PeriapsisDirection=new(1,0,0),TransverseDirection=new(0,1,0),Quality=SystemOrbitQuality.NativeVerified}; Check.That(valid.Renderable); foreach(var invalid in new[]{valid with{Quality=SystemOrbitQuality.ElementsOnly},valid with{SemiMajorAxisMetres=double.PositiveInfinity}, valid with{Eccentricity=1},valid with{PeriapsisDirection=new(2,0,0)},valid with{TransverseDirection=new(1,0,0)}, valid with{ParentWorldAddress=102},valid with{ParentIdentity=null}}) Check.That(!invalid.Renderable); } [Test]public void MalformedParentShapeIsIgnoredInsteadOfCrashingJournalReplay() { var source=new JournalSystemOrbitCatalogue(); source.Apply(JsonSerializer.SerializeToElement(new{@event="Scan",timestamp=At,SystemAddress=7,BodyID=2,BodyName=Planet.Name,Parents=new[]{new{Star="invalid"}}})); Check.That(source.Bind(Snapshot(Planet)).Single().JournalBodyId is null); } [Test]public void OriginalAdLeonisScanKeepsObservedEpochUnitsAndAnUnlocatedBarycentreExplicit() { using var doc=JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory,"Fixtures","ad-leonis-orbit-scans.json"))); var source=new JournalSystemOrbitCatalogue();foreach(var scan in doc.RootElement.EnumerateArray())source.Apply(scan); var star=Body(101,"AD Leonis A",SystemLocationCategory.Star); var planet=Body(102,"AD Leonis A 4",SystemLocationCategory.Planet); var snapshot=new CurrentSystemSnapshot(5368172661576,"AD Leonis",42,1,At,[star,planet]); var result=source.Bind(snapshot);var stellar=result.Single(orbit=>orbit.ChildWorldAddress==101); Check.Equal(8019258618354.797,stellar.SemiMajorAxisMetres);Check.Equal(.169544,stellar.Eccentricity); Check.That(stellar.ParentWorldAddress is null&&stellar.JournalParents[0].Kind=="Null"); var planetary=result.Single(orbit=>orbit.ChildWorldAddress==102); Check.Equal(star.WorldAddress,planetary.ParentWorldAddress);Check.Equal(298840373754.50134,planetary.SemiMajorAxisMetres); Check.Equal(199594521.522522,planetary.PeriodSeconds);Check.Equal(-8.789576,planetary.AscendingNodeDegrees); Check.Equal(DateTimeOffset.Parse("2026-10-24T03:57:00Z"),planetary.Epoch); Check.That(result.All(orbit=>orbit.Quality==SystemOrbitQuality.ElementsOnly&&!orbit.Renderable)); } }