test(vt): pin the route store's legacy-JSON migration (round 2 step 4)
Adds the same migrate-and-delete / counterpart-already-exists coverage for MossTankRouteProfileStore that Settings and Meta already had, completing step 4's "pin with a test per store" requirement. Also corrects an assumption in the route store's own waypoint model: metaf's "pnt" node format is bare x/y/z (three doubles, MetafSerializer.cs:217) with no cell id at all for any waypoint type, not just "jmp" — so the migration test checks EastWest/NorthSouth instead of CellId. Mutation shown to fail: MossTankRouteProfileStore.MigrateLegacyIfNeeded short-circuited to a no-op made RouteStoreMigratesLegacyJsonProfileToAfAndDeletesTheJsonKey fail (LoadCurrent returned false, nothing to load); restored, it passes along with the companion counterpart-exists test. 593 MossTank tests passing (was 591). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
a5d52bfb07
commit
e5c4912b6d
1 changed files with 77 additions and 0 deletions
|
|
@ -458,6 +458,82 @@ public sealed class NavigationTests
|
|||
Assert.Equal(-3, target.DoorLockpickExcessThreshold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reproduces MossTankRouteProfileStore's pre-cutover by-character JSON
|
||||
/// hash key (its own <c>LegacyProfileKey</c> is private; the format is
|
||||
/// the migration contract itself, reproduced verbatim here).
|
||||
/// </summary>
|
||||
private static string LegacyRouteByCharacterKey(string characterName)
|
||||
{
|
||||
string identity = "char:" + characterName.Trim().ToUpperInvariant();
|
||||
string hash = Convert.ToHexString(
|
||||
System.Security.Cryptography.SHA256.HashData(
|
||||
System.Text.Encoding.UTF8.GetBytes(identity)));
|
||||
return $"profiles/route/{hash}.json";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RouteStoreMigratesLegacyJsonProfileToAfAndDeletesTheJsonKey()
|
||||
{
|
||||
var storage = new MemoryStorage();
|
||||
string legacyKey = LegacyRouteByCharacterKey("Barris");
|
||||
storage.Text[legacyKey] = """
|
||||
{
|
||||
"Mode": 1,
|
||||
"Waypoints": [
|
||||
{ "Type": 0, "EastWest": 5.0, "NorthSouth": 6.0 }
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
var store = new MossTankRouteProfileStore(new FakeHost(new FakeAutomation(), storage));
|
||||
Assert.True(store.BindCharacter("Barris"));
|
||||
var target = new NavigationSettings();
|
||||
Assert.True(store.LoadCurrent(target, MetafSerializer.NoOpSpells.Instance));
|
||||
|
||||
Assert.False(storage.Text.ContainsKey(legacyKey));
|
||||
Assert.Equal(RouteMode.Linear, target.Mode);
|
||||
RouteWaypoint waypoint = Assert.Single(target.Waypoints);
|
||||
// metaf's "pnt" node format is bare x/y/z (three doubles) — no cell
|
||||
// id at all, for any waypoint type — so only the coordinates
|
||||
// round-trip.
|
||||
Assert.Equal(5.0d, waypoint.Position.EastWest, precision: 3);
|
||||
Assert.Equal(6.0d, waypoint.Position.NorthSouth, precision: 3);
|
||||
|
||||
// Idempotent second run: nothing left to migrate.
|
||||
var reopened = new MossTankRouteProfileStore(new FakeHost(new FakeAutomation(), storage));
|
||||
Assert.True(reopened.BindCharacter("Barris"));
|
||||
var reloaded = new NavigationSettings();
|
||||
Assert.True(reopened.LoadCurrent(reloaded, MetafSerializer.NoOpSpells.Instance));
|
||||
Assert.Single(reloaded.Waypoints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RouteStoreLeavesLegacyJsonUntouchedWhenAfCounterpartExists()
|
||||
{
|
||||
var storage = new MemoryStorage();
|
||||
string legacyKey = LegacyRouteByCharacterKey("Barris");
|
||||
storage.Text[legacyKey] = """{ "Mode": 1, "Waypoints": [] }""";
|
||||
string realKey = "nav_" + VtankProfileDirectory.AutoCharacterFileName(
|
||||
"Barris", string.Empty, "af");
|
||||
var real = new NavigationSettings { Mode = RouteMode.Circular };
|
||||
real.Waypoints.Add(new RouteWaypoint
|
||||
{
|
||||
Type = RouteWaypointType.Point,
|
||||
Position = new PluginNavigationPosition(0x00010001u, 1d, 2d, 0d, 0f, true),
|
||||
});
|
||||
storage.Text[realKey] = MetafSerializer.SaveNav(real);
|
||||
|
||||
var store = new MossTankRouteProfileStore(new FakeHost(new FakeAutomation(), storage));
|
||||
Assert.True(store.BindCharacter("Barris"));
|
||||
var target = new NavigationSettings();
|
||||
Assert.True(store.LoadCurrent(target, MetafSerializer.NoOpSpells.Instance));
|
||||
|
||||
Assert.True(storage.Text.ContainsKey(legacyKey));
|
||||
Assert.Equal(RouteMode.Circular, target.Mode);
|
||||
Assert.Single(target.Waypoints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FollowModeRouteRoundTripsTheFollowTargetThroughAf()
|
||||
{
|
||||
|
|
@ -619,6 +695,7 @@ public sealed class NavigationTests
|
|||
private sealed class MemoryStorage : IPluginStorage
|
||||
{
|
||||
private readonly Dictionary<string, string> _text = new(StringComparer.Ordinal);
|
||||
public Dictionary<string, string> Text => _text;
|
||||
public bool IsAvailable => true;
|
||||
public string? ReadText(string key) =>
|
||||
_text.TryGetValue(key, out string? value) ? value : null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue