using System.Text.Json;
using System.Text.Json.Nodes;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Navigation;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public sealed class GalaxyCurrentRouteChoicesTests
{
private static readonly int[] Ids=[2,3,4,6,7,8,10];
private static JsonObject Wire(bool known=true)
{
var categories=new JsonArray();
foreach(var id in Ids)categories.Add(new JsonObject{["categoryId"]=id,["affectsRoutePlotting"]=known?JsonValue.Create(false):null,
["choices"]=known?new JsonArray(new JsonObject{["index"]=1,["settingsName"]="Lawless",["key"]="lawless",["selected"]=false},
new JsonObject{["index"]=0,["settingsName"]="Anarchy",["key"]="anarchy",["selected"]=true}):null});
return new(){["scope"]="current-original-route-filter-choices",["capturedAtTick"]="9007199254740995",["contextSystemAddress"]="9007199254740993",["categories"]=categories};
}
private static GalaxyCurrentRouteChoices Parse(JsonObject value)=>GalaxyCurrentRouteChoices.FromWire(Check.Json(value.ToJsonString()))!;
[Test] public void ActualIndicesNamesSelectionsAndFalseRouteFlagRemainIndependentForAllSevenCategories()
{
var choices=Parse(Wire());Check.Equal(9007199254740995UL,choices.CapturedAtTick);Check.Equal(9007199254740993L,choices.ContextSystemAddress);
Check.That(choices.Categories.Select(c=>c.CategoryId).SequenceEqual(Ids));
foreach(var category in choices.Categories)
{
Check.That(category.AffectsRoutePlotting==false&&category.Choices is{Count:2});
Check.Equal("Anarchy",category.Choices![0].SettingsName);Check.Equal("anarchy",category.Choices[0].Key);
Check.That(category.Choices[0].Selected&&!category.Choices[1].Selected);Check.Equal(1,category.Choices[1].Index);
}
}
[Test] public void MergingLiveNamesDoesNotEditOrdinaryValuesSavedValuesOrUnrelatedStarRouteSettings()
{
IReadOnlySet<string> values=new HashSet<string>{"anarchy"};var saved=new Dictionary<GalaxyFilterCategory,IReadOnlySet<string>?>{{GalaxyFilterCategory.Economy,new HashSet<string>{"industrial"}}};
var filter=new GalaxyMapFilter{Category=GalaxyFilterCategory.Security,Values=values,SavedValues=saved,
OriginalRouteFilters=new Dictionary<GalaxyFilterCategory,bool>{{GalaxyFilterCategory.StarClass,true}}};
var merged=Parse(Wire()).MergeInto(filter);
Check.That(ReferenceEquals(values,merged.Values)&&ReferenceEquals(saved,merged.SavedValues)&&merged.OriginalRouteFilters[GalaxyFilterCategory.StarClass]);
Check.That(merged.OriginalRouteOptionKeys[GalaxyFilterCategory.Security].SetEquals(["anarchy","lawless"]));
Check.That(merged.OriginalRouteSelectedKeys[GalaxyFilterCategory.Security].SetEquals(["anarchy"]));
Check.That(GameRouteMapFilter.TryFromFilter(merged,true,out var route,out _));Check.That(!route!.AffectsRoutePlotting&&route.SelectedKeys.SequenceEqual(["anarchy"]));
}
[Test] public void StructurallyIdenticalFreshSnapshotsReturnTheSameFilterRecord()
{
var choices=Parse(Wire());var filter=choices.MergeInto(new GalaxyMapFilter());
var fresh=Parse(Wire()) with{CapturedAtTick=choices.CapturedAtTick+1};
Check.That(ReferenceEquals(filter,fresh.MergeInto(filter)));
Check.That(new GalaxyRouteChoicesSourceKey(42,10,100)!=new GalaxyRouteChoicesSourceKey(42,11,100));
Check.That(new GalaxyRouteChoicesSourceKey(42,10,100)!=new GalaxyRouteChoicesSourceKey(43,10,100));
Check.Equal("42|10|100",new GalaxyRouteChoicesSourceKey(42,10,100).ToString());
}
[Test] public void ExplicitUnknownClearsOnlyLiveManagedCategoriesAndCannotInventAnEmptyProvider()
{
var known=Parse(Wire()).MergeInto(new(){Values=new HashSet<string>{"government"}});var oldValues=known.Values;
var cleared=Parse(Wire(false)).MergeInto(known);
Check.That(cleared.OriginalRouteOptionKeys.Count==0&&cleared.OriginalRouteSelectedKeys.Count==0&&cleared.OriginalRouteFilters.Count==0&&ReferenceEquals(oldValues,cleared.Values));
Check.That(ReferenceEquals(cleared,GalaxyCurrentRouteChoices.ClearLive(cleared))&&GalaxyCurrentRouteChoices.FromWire(Check.Json("null")) is null);
var empty=Wire();empty["categories"]![0]!["choices"]=new JsonArray();
Check.Throws<InvalidDataException>(()=>Parse(empty));
}
[Test] public void MalformedSourcesNamesIndicesFlagsAndCategoryCoverageAreRejected()
{
foreach(var mutate in new Action<JsonObject>[]
{
j=>j["contextSystemAddress"]=9007199254740993L,j=>j["contextSystemAddress"]="0",j=>j["categories"]!.AsArray().RemoveAt(0),
j=>j["categories"]![0]!["categoryId"]=5,j=>j["categories"]![1]!["categoryId"]=2,
j=>j["categories"]![0]!["affectsRoutePlotting"]=null,j=>j["categories"]![0]!["choices"]![0]!["index"]=0,
j=>j["categories"]![0]!["choices"]![0]!["index"]=2,j=>j["categories"]![0]!["choices"]![0]!["key"]="anarchy",
j=>j["categories"]![0]!["choices"]![0]!["selected"]=1
}){var input=Wire();mutate(input);Check.Throws<InvalidDataException>(()=>Parse(input));}
}
[Test] public void CatalogueFieldAndSeparateCapabilityRemainOptionalForLegacyProfiles()
{
var catalog=new NativeGalaxyCatalog(42);catalog.ApplyProtocolDescription(Check.Json("{\"processId\":42,\"galaxyCatalogueProfile\":true}"));Check.That(!catalog.SupportsRouteFilterChoices);
catalog.ApplyProtocolDescription(Check.Json("{\"processId\":42,\"routeFilterChoices\":true}"));Check.That(catalog.SupportsRouteFilterChoices);
var profile=new JsonObject{["origin"]=new JsonArray(0,0,0),["referenceKind"]="camera-eye",["routeFilterChoices"]=Wire()};
Check.That(NativeGalaxyCatalog.ReadCatalogueProfile(Check.Json(profile.ToJsonString())).CurrentRouteChoices is{Categories.Count:7});
profile.Remove("routeFilterChoices");Check.That(NativeGalaxyCatalog.ReadCatalogueProfile(Check.Json(profile.ToJsonString())).CurrentRouteChoices is null);
}
}
using System.Text.Json;
using System.Text.Json.Nodes;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Navigation;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public sealed class GalaxyCurrentRouteChoicesTests
{
private static readonly int[] Ids=[2,3,4,6,7,8,10];
private static JsonObject Wire(bool known=true)
{
var categories=new JsonArray();
foreach(var id in Ids)categories.Add(new JsonObject{["categoryId"]=id,["affectsRoutePlotting"]=known?JsonValue.Create(false):null,
["choices"]=known?new JsonArray(new JsonObject{["index"]=1,["settingsName"]="Lawless",["key"]="lawless",["selected"]=false},
new JsonObject{["index"]=0,["settingsName"]="Anarchy",["key"]="anarchy",["selected"]=true}):null});
return new(){["scope"]="current-original-route-filter-choices",["capturedAtTick"]="9007199254740995",["contextSystemAddress"]="9007199254740993",["categories"]=categories};
}
private static GalaxyCurrentRouteChoices Parse(JsonObject value)=>GalaxyCurrentRouteChoices.FromWire(Check.Json(value.ToJsonString()))!;
[Test] public void ActualIndicesNamesSelectionsAndFalseRouteFlagRemainIndependentForAllSevenCategories()
{
var choices=Parse(Wire());Check.Equal(9007199254740995UL,choices.CapturedAtTick);Check.Equal(9007199254740993L,choices.ContextSystemAddress);
Check.That(choices.Categories.Select(c=>c.CategoryId).SequenceEqual(Ids));
foreach(var category in choices.Categories)
{
Check.That(category.AffectsRoutePlotting==false&&category.Choices is{Count:2});
Check.Equal("Anarchy",category.Choices![0].SettingsName);Check.Equal("anarchy",category.Choices[0].Key);
Check.That(category.Choices[0].Selected&&!category.Choices[1].Selected);Check.Equal(1,category.Choices[1].Index);
}
}
[Test] public void MergingLiveNamesDoesNotEditOrdinaryValuesSavedValuesOrUnrelatedStarRouteSettings()
{
IReadOnlySet<string> values=new HashSet<string>{"anarchy"};var saved=new Dictionary<GalaxyFilterCategory,IReadOnlySet<string>?>{{GalaxyFilterCategory.Economy,new HashSet<string>{"industrial"}}};
var filter=new GalaxyMapFilter{Category=GalaxyFilterCategory.Security,Values=values,SavedValues=saved,
OriginalRouteFilters=new Dictionary<GalaxyFilterCategory,bool>{{GalaxyFilterCategory.StarClass,true}}};
var merged=Parse(Wire()).MergeInto(filter);
Check.That(ReferenceEquals(values,merged.Values)&&ReferenceEquals(saved,merged.SavedValues)&&merged.OriginalRouteFilters[GalaxyFilterCategory.StarClass]);
Check.That(merged.OriginalRouteOptionKeys[GalaxyFilterCategory.Security].SetEquals(["anarchy","lawless"]));
Check.That(merged.OriginalRouteSelectedKeys[GalaxyFilterCategory.Security].SetEquals(["anarchy"]));
Check.That(GameRouteMapFilter.TryFromFilter(merged,true,out var route,out _));Check.That(!route!.AffectsRoutePlotting&&route.SelectedKeys.SequenceEqual(["anarchy"]));
}
[Test] public void StructurallyIdenticalFreshSnapshotsReturnTheSameFilterRecord()
{
var choices=Parse(Wire());var filter=choices.MergeInto(new GalaxyMapFilter());
var fresh=Parse(Wire()) with{CapturedAtTick=choices.CapturedAtTick+1};
Check.That(ReferenceEquals(filter,fresh.MergeInto(filter)));
Check.That(new GalaxyRouteChoicesSourceKey(42,10,100)!=new GalaxyRouteChoicesSourceKey(42,11,100));
Check.That(new GalaxyRouteChoicesSourceKey(42,10,100)!=new GalaxyRouteChoicesSourceKey(43,10,100));
Check.Equal("42|10|100",new GalaxyRouteChoicesSourceKey(42,10,100).ToString());
}
[Test] public void ExplicitUnknownClearsOnlyLiveManagedCategoriesAndCannotInventAnEmptyProvider()
{
var known=Parse(Wire()).MergeInto(new(){Values=new HashSet<string>{"government"}});var oldValues=known.Values;
var cleared=Parse(Wire(false)).MergeInto(known);
Check.That(cleared.OriginalRouteOptionKeys.Count==0&&cleared.OriginalRouteSelectedKeys.Count==0&&cleared.OriginalRouteFilters.Count==0&&ReferenceEquals(oldValues,cleared.Values));
Check.That(ReferenceEquals(cleared,GalaxyCurrentRouteChoices.ClearLive(cleared))&&GalaxyCurrentRouteChoices.FromWire(Check.Json("null")) is null);
var empty=Wire();empty["categories"]![0]!["choices"]=new JsonArray();
Check.Throws<InvalidDataException>(()=>Parse(empty));
}
[Test] public void MalformedSourcesNamesIndicesFlagsAndCategoryCoverageAreRejected()
{
foreach(var mutate in new Action<JsonObject>[]
{
j=>j["contextSystemAddress"]=9007199254740993L,j=>j["contextSystemAddress"]="0",j=>j["categories"]!.AsArray().RemoveAt(0),
j=>j["categories"]![0]!["categoryId"]=5,j=>j["categories"]![1]!["categoryId"]=2,
j=>j["categories"]![0]!["affectsRoutePlotting"]=null,j=>j["categories"]![0]!["choices"]![0]!["index"]=0,
j=>j["categories"]![0]!["choices"]![0]!["index"]=2,j=>j["categories"]![0]!["choices"]![0]!["key"]="anarchy",
j=>j["categories"]![0]!["choices"]![0]!["selected"]=1
}){var input=Wire();mutate(input);Check.Throws<InvalidDataException>(()=>Parse(input));}
}
[Test] public void CatalogueFieldAndSeparateCapabilityRemainOptionalForLegacyProfiles()
{
var catalog=new NativeGalaxyCatalog(42);catalog.ApplyProtocolDescription(Check.Json("{\"processId\":42,\"galaxyCatalogueProfile\":true}"));Check.That(!catalog.SupportsRouteFilterChoices);
catalog.ApplyProtocolDescription(Check.Json("{\"processId\":42,\"routeFilterChoices\":true}"));Check.That(catalog.SupportsRouteFilterChoices);
var profile=new JsonObject{["origin"]=new JsonArray(0,0,0),["referenceKind"]="camera-eye",["routeFilterChoices"]=Wire()};
Check.That(NativeGalaxyCatalog.ReadCatalogueProfile(Check.Json(profile.ToJsonString())).CurrentRouteChoices is{Categories.Count:7});
profile.Remove("routeFilterChoices");Check.That(NativeGalaxyCatalog.ReadCatalogueProfile(Check.Json(profile.ToJsonString())).CurrentRouteChoices is null);
}
}