using XFEEDToolBox.Core.Galaxy; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture] public class GalaxyJournalMarkersTests { [Test] public void ActualQueryEvidenceRoundTripsWithPersistentGalaxyCacheWithoutChangingUnknownFacts() { var system=new GalacticSystem("Measured",new(1,2,3),27){Data=new(){AccountFid="F1",ConfirmedQueries=new HashSet{"Power:actualcontroller"},PersonalMarkers=["bookmark"],BookmarkStyles=[new(7,false),new(2,true)],Visited=true}}; var json=System.Text.Json.JsonSerializer.Serialize(system); var loaded=System.Text.Json.JsonSerializer.Deserialize(json)!; Check.That(loaded.Data.ConfirmedQueries.Contains("Power:actualcontroller"));Check.That(loaded.Data.Visited==true&&loaded.Data.Population is null);Check.Equal("bookmark",loaded.Data.PersonalMarkers![0]); Check.That(loaded.Data.BookmarkStyles!.SequenceEqual(system.Data.BookmarkStyles!));Check.That(loaded.Data.ForAccount("F2").BookmarkStyles is null); Check.That(ReferenceEquals(system.Data,system.Data.Merge(new(){BookmarkStyles=[new(7,false),new(2,true)]}))); } [Test] public void MissionMarkersFollowRealRedirectCompletionSnapshotAndExpiry() { var markers=new GalaxyJournalMarkers();var at=DateTimeOffset.Parse("2026-10-23T00:00:00Z"); markers.Apply(Check.Json("""{"event":"MissionAccepted","MissionID":11,"DestinationSystem":"Original","Expiry":"2026-10-23T01:00:00Z"}"""),at); markers.Apply(Check.Json("""{"event":"MissionAccepted","MissionID":12,"DestinationSystem":"Finished","Expiry":"2026-10-23T01:00:00Z"}"""),at); markers.Apply(Check.Json("""{"event":"MissionRedirected","MissionID":11,"NewDestinationSystem":"Redirected"}"""),at); markers.Apply(Check.Json("""{"event":"MissionCompleted","MissionID":12}"""),at); Check.That(markers.MissionSystems(at).SetEquals(new[]{"Redirected"})); markers.Apply(Check.Json("""{"event":"Missions","Active":[{"MissionID":11,"Expires":600},{"MissionID":13,"Expires":600}]}"""),at); Check.That(markers.MissionSystems(at.AddSeconds(599)).SetEquals(new[]{"Redirected"})); Check.Equal(0,markers.MissionSystems(at.AddSeconds(601)).Count); markers.Apply(Check.Json("""{"event":"Missions","Active":[]}"""),at);Check.Equal(0,markers.MissionSystems(at).Count); markers.Apply(Check.Json("""{"event":"MissionAccepted","MissionID":14,"DestinationSystem":"No optional expiry"}"""),at);Check.That(markers.MissionSystems(at).Contains("No optional expiry")); markers.Apply(Check.Json("""{"event":"MissionAbandoned","MissionID":14}"""),at);Check.Equal(0,markers.MissionSystems(at).Count); } [Test] public void OnlySubscribedUnfinishedUnexpiredCommunityGoalsCreateMarkers() { var markers=new GalaxyJournalMarkers();var at=DateTimeOffset.Parse("2026-10-23T00:00:00Z"); markers.Apply(Check.Json("""{"event":"CommunityGoal","CurrentGoals":[{"CGID":1,"SystemName":"Active","IsComplete":false,"Expiry":"2026-10-23T01:00:00Z"},{"CGID":2,"SystemName":"Finished","IsComplete":true,"Expiry":"2026-10-23T01:00:00Z"},{"CGID":3,"SystemName":"Expired","IsComplete":false,"Expiry":"2026-10-22T01:00:00Z"}]}"""),at); Check.That(markers.CommunityGoalSystems(at).SetEquals(new[]{"Active"})); markers.Apply(Check.Json("""{"event":"CommunityGoalDiscard","CGID":1}"""),at);Check.Equal(0,markers.CommunityGoalSystems(at).Count); } [Test] public void AcquiredMapDataIsIndependentOfVisitationAndTimestampDoesNotProveTradeValidity() { var map=GalaxySystemData.FromAcquisition(Check.Json("""{"event":"BuyExplorationData","System":"Remote","timestamp":"2026-10-23T00:00:00Z"}"""))!; Check.That(map.ExplorationPurchasedAt is not null&&map.Exploration is null&&map.Visited is null); var trade=GalaxySystemData.FromAcquisition(Check.Json("""{"event":"BuyTradeData","System":"Remote","timestamp":"2026-10-23T00:00:00Z"}"""))!; Check.That(trade.TradeDataPurchasedAt is not null&&trade.TradeData is null&&trade.Visited is null); var historicalCache=new GalaxySystemData{AccountFid="F1",Exploration="havedata",ExplorationPurchasedAt=map.ExplorationPurchasedAt}; var corrected=historicalCache.ForAccount("F1");Check.That(corrected.Exploration is null&&corrected.ExplorationPurchasedAt is not null); } [Test] public void AccountSwitchRemovesPrivateFactsAndDynamicMarkersCanBeClearedWithoutLosingWorldFacts() { var data=new GalaxySystemData{AccountFid="F1",Visited=true,Government="Democracy",PersonalMarkers=["mission"],TradeDataRecordedAt=DateTimeOffset.UtcNow,ConfirmedQueries=new HashSet{"Visited:visited","Exploration:havedata","TradeData:havedata","Markers:bookmark","Government:democracy"}}; var foreign=data.ForAccount("F2");Check.That(foreign.Visited is null&&foreign.PersonalMarkers is null&&foreign.TradeDataRecordedAt is null);Check.Equal("Democracy",foreign.Government); Check.That(foreign.ConfirmedQueries.SetEquals(new[]{"Government:democracy"})); var done=data.Merge(new(){PersonalMarkers=[]});Check.That(done.PersonalMarkers is {Count:0});Check.That(done.Visited==true); Check.That(ReferenceEquals(data,data.Merge(new(){Services=null,Visited=true,PersonalMarkers=["mission"]}))); } [Test] public void VerifiedOwnedColonyIsIndependentOfReportedColonialStatusAndNeverMakesOtherSystemsUnclaimed() { var filter=new GalaxyMapFilter{Mode=GalaxyMapMode.Colonisation,Category=GalaxyFilterCategory.Colonisation,Values=new HashSet{"localplayerclaimed"}}; var data=new GalaxySystemData{AccountFid="F1",OwnedColony=true,Colonisation="colonised"}; var merged=data.Merge(new(){Colonisation="beingcolonised"});Check.That(merged.OwnedColony==true);Check.That(filter.Matches(new("Owned",new(0,0,0)){Data=merged})); var unknown=new GalacticSystem("Unknown",new(0,0,0)){Data=new(){Colonisation="colonised"}};Check.That(!filter.Matches(unknown));Check.That(filter.IsUnknown(unknown));Check.That((filter with{IncludeUnknown=true}).Matches(unknown)); var switched=merged.ForAccount("F2");Check.That(switched.OwnedColony is null);Check.Equal("beingcolonised",switched.Colonisation); } [Test] public void ExplicitNativeSnapshotNullsClearOldClassificationsAndCannotSurviveOriginPidAccountOrDiskChanges() { var first=new GalaxyNativeSystemFacts("F1",27,100,Visited:false,Exploration:"dataavailable",TradeData:"havedata",WithinDataRange:true,MetadataStatus:3); var data=new GalaxySystemData().Merge(new(){NativeFilterFacts=first}); var trade=new GalaxyMapFilter{Mode=GalaxyMapMode.Map,Category=GalaxyFilterCategory.TradeData,Values=new HashSet{"havedata"}}; Check.That(trade.Matches(new("Measured",new(0,0,0)){Data=data}));Check.That(ReferenceEquals(data,data.Merge(new(){NativeFilterFacts=first with{}}))); var unknown=data.Merge(new(){NativeFilterFacts=first with{Visited=null,Exploration=null,TradeData=null,WithinDataRange=false,MetadataStatus=null}}); Check.That(!trade.Matches(new("Measured",new(0,0,0)){Data=unknown}));Check.That(!trade.Matches(new("Measured",new(0,0,0)){Data=unknown with{ConfirmedQueries=new HashSet{trade.QueryKey}}})); Check.That(unknown.NativeFilterFacts is {TradeData:null,Exploration:null,Visited:null,WithinDataRange:false}); Check.That(data.ForNativeContext("F1",27,101).NativeFilterFacts is null);Check.That(data.ForNativeContext("F1",28,100).NativeFilterFacts is null);Check.That(data.ForNativeContext("F2",27,100).NativeFilterFacts is null);Check.That(data.ForNativeContext("F1",null,100).NativeFilterFacts is null); var disk=System.Text.Json.JsonSerializer.Deserialize(System.Text.Json.JsonSerializer.Serialize(data))!;Check.That(disk.NativeFilterFacts is null&&disk.Exploration is null&&disk.TradeData is null); var untrustedNegative=new GalaxySystemData{AccountFid="F1",Visited=false,ConfirmedQueries=new HashSet{"Visited:notvisited"}}.ForNativeContext("F1",27,100); Check.That(untrustedNegative.Visited is null&&!untrustedNegative.ConfirmedQueries.Contains("Visited:notvisited")); Check.That(GalaxyMapFilter.Keys(new("Original",new(0,0,0)){Data=data.ForNativeContext("F1",27,100)},GalaxyFilterCategory.Visited)!.Contains("notvisited")); var visitedHistory=new GalaxySystemData{AccountFid="F1",Visited=true}.Merge(new(){NativeFilterFacts=unknown.NativeFilterFacts});Check.That(GalaxyMapFilter.Keys(new("Measured",new(0,0,0)){Data=visitedHistory},GalaxyFilterCategory.Visited)!.Contains("visited")); Check.That(new GalaxySystemData{Visited=true}.Merge(new(){NativeFilterFacts=unknown.NativeFilterFacts}).Visited is null); var switched=new GalaxySystemData{AccountFid="F1",Visited=true,OwnedColony=true,Government="Democracy"}.Merge(new(){NativeFilterFacts=first with{AccountFid="F2",Visited=null}}); Check.That(switched.Visited is null&&switched.OwnedColony is null);Check.Equal("Democracy",switched.Government); } [Test] public void NativeColonisationEligibilityUsesActualRulesAndClaimStatusNeverInventsCommanderOwnership() { var filter=new GalaxyMapFilter{Mode=GalaxyMapMode.Colonisation,Category=GalaxyFilterCategory.Colonisation,Values=new HashSet{"unclaimed"}}; GalacticSystem Original(int? state)=>new("Original",new(0,0,0)){Data=new(){AccountFid="F1",NativeFilterFacts=new("F1",27,100,ColonisationStatus:state)}}; Check.That(filter.Matches(Original(0)));Check.That(!filter.Matches(Original(1)));Check.That(!filter.IsUnknown(Original(1))); var denied=filter with{Values=new HashSet{"excluded"}}; foreach(var state in new[]{5,6,7,8,9,10,11})Check.That(denied.Matches(Original(state))&&!filter.Matches(Original(state))&&!filter.IsUnknown(Original(state))); foreach(var state in new int?[]{null,12,13,14})Check.That(!denied.Matches(Original(state))&&denied.IsUnknown(Original(state))); var owned=filter with{Values=new HashSet{"localplayerclaimed"}}; foreach(var state in new[]{1,2,3}){var claim=Original(state);Check.That(!owned.Matches(claim)&&owned.IsUnknown(claim));Check.That(owned.Matches(claim with{Data=claim.Data with{OwnedColony=true}}));} var populated=filter with{Values=new HashSet{"alreadypopulated"}};Check.That(populated.Matches(Original(3))&&populated.Matches(Original(4))); var cleared=Original(0);cleared=cleared with{Data=cleared.Data.Merge(new(){NativeFilterFacts=cleared.Data.NativeFilterFacts! with{ColonisationStatus=null}})};Check.That(!filter.Matches(cleared)); } [Test] public void DiskCacheAndOldQueryProofCannotResurrectLiveSocialPositions() { var live=new GalaxySystemData{AccountFid="F1",PersonalMarkers=["bookmark","friend","wing"],ConfirmedQueries=new HashSet{"Markers:friend"}}; var disk=System.Text.Json.JsonSerializer.Deserialize(System.Text.Json.JsonSerializer.Serialize(live))!; Check.That(disk.PersonalMarkers!.SequenceEqual(new[]{"bookmark"})); var oldDisk=System.Text.Json.JsonSerializer.Deserialize("""{"AccountFid":"F1","PersonalMarkers":["friend","wing","bookmark"]}""")!;Check.That(oldDisk.PersonalMarkers!.SequenceEqual(new[]{"bookmark"})); var friend=new GalaxyMapFilter{Mode=GalaxyMapMode.Map,Category=GalaxyFilterCategory.Markers,Values=new HashSet{"friend"}}; Check.That(friend.Matches(new("Actual",new(0,0,0)){Data=live}));Check.That(!friend.Matches(new("Old",new(0,0,0)){Data=disk})); } [Test] public void OriginalRecentActivityThresholdUsesExactScopeAndAcceptsARealZero() { var filter=new GalaxyMapFilter{Mode=GalaxyMapMode.Powerplay,Category=GalaxyFilterCategory.PowerActivityEligibility,ActivityPowerName="Actual Power",ActivityInternalPowerId=4,Activity=GalaxyPowerActivity.Acquiring,UseMinimumActivity=true,MinimumActivity=.5}; GalacticSystem Evidence(GalaxyPowerActivityEvidence? evidence)=>new("Measured",new(0,0,0)){Data=new(){PowerActivities=["reinforcement","undermining"],NativeFilterFacts=new("F1",27,100,PowerActivity:evidence),ConfirmedQueries=new HashSet{filter.QueryKey}}}; Check.That(!filter.Matches(Evidence(null)));Check.That(!filter.Matches(Evidence(new("Other Power",GalaxyPowerActivity.Acquiring,.7,4)))); Check.That(!filter.Matches(Evidence(new("Actual Power",GalaxyPowerActivity.Undermining,.7,4))));Check.That(!filter.Matches(Evidence(new("Actual Power",GalaxyPowerActivity.Acquiring,.7,1)))); var measured=Evidence(new("Actual Power",GalaxyPowerActivity.Acquiring,.7,4));Check.That(filter.Matches(measured)); Check.That(!(filter with{MinimumActivity=.8}).Matches(measured));Check.That(filter.Matches(Evidence(new("Actual Power",GalaxyPowerActivity.Acquiring,2,4)))); var zero=Evidence(new("Actual Power",GalaxyPowerActivity.Acquiring,0,4));Check.That(!(filter with{MinimumActivity=.1}).Matches(zero));Check.That((filter with{MinimumActivity=0}).Matches(zero)); var missing=Evidence(new("Actual Power",GalaxyPowerActivity.Acquiring,null,4));Check.That(!filter.Matches(missing)&&filter.IsUnknown(missing));Check.That((filter with{IncludeUnknown=true}).Matches(missing)); Check.That(!(filter with{ActivityPowerName=null,IncludeUnknown=true}).Matches(measured));Check.That(!(filter with{MinimumActivity=double.NaN}).ActivityQueryReady); var disabled=filter with{UseMinimumActivity=false,ActivityPowerName=null,ActivityInternalPowerId=null};Check.That(disabled.Matches(Evidence(null))&&!disabled.IsUnknown(Evidence(null)));Check.That(disabled.Evaluate(Evidence(null)).Matches); Check.That(filter.QueryKey!=(filter with{Activity=GalaxyPowerActivity.BeingUndermined}).QueryKey&&filter.QueryKey!=disabled.QueryKey); Check.That(!filter.Matches(measured with{Data=measured.Data.ForNativeContext("F1",27,101)})); var all=filter with{ActivityAllPowers=true,ActivityPowerName=null,ActivityInternalPowerId=null}; Check.That(all.Matches(Evidence(new("",GalaxyPowerActivity.Acquiring,.7,AllPowers:true))));Check.That(!all.Matches(measured));Check.That(all.QueryKey!=filter.QueryKey); } }