test(saves): lock unresolved-type handling in v4 and legacy v3 loading
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
98b054ad8d
commit
50d02cffec
1 changed files with 102 additions and 0 deletions
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace Server.Tests;
|
||||
|
|
@ -47,4 +49,104 @@ public class GenericEntityPersistenceTypeTableTests
|
|||
persistence.Unregister();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnresolvedTableEntrySkipsItsRecordsAfterConfirmation()
|
||||
{
|
||||
var previousAssemblies = AssemblyHandler.Assemblies;
|
||||
AssemblyHandler.Assemblies = [.. previousAssemblies ?? [], typeof(RoundTripEntity).Assembly];
|
||||
|
||||
var dir = Path.Combine(Path.GetTempPath(), $"muo-typetable-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(Path.Combine(dir, "TypeTable"));
|
||||
|
||||
TypeTablePersistence loaded = null;
|
||||
var previousIn = Console.In;
|
||||
|
||||
try
|
||||
{
|
||||
// Hand-write a v4 idx: two table entries (one bogus), one record per type.
|
||||
using (var idx = new FileBufferWriter(Path.Combine(dir, "TypeTable", "TypeTable.idx")))
|
||||
{
|
||||
idx.Write(4); // version
|
||||
idx.Write(2); // type table count
|
||||
idx.WriteRaw(typeof(RoundTripEntity).FullName); // index 0: resolvable
|
||||
idx.WriteRaw("Server.Tests.DoesNotExistAnymore"); // index 1: bogus
|
||||
idx.Write(2); // record count
|
||||
idx.Write((ushort)0); // record 1: real type
|
||||
idx.Write(1u); // serial
|
||||
idx.Write(DateTime.UtcNow.Ticks);
|
||||
idx.Write(0L); // position
|
||||
idx.Write(4); // length
|
||||
idx.Write((ushort)1); // record 2: bogus type
|
||||
idx.Write(2u);
|
||||
idx.Write(DateTime.UtcNow.Ticks);
|
||||
idx.Write(4L);
|
||||
idx.Write(4);
|
||||
}
|
||||
|
||||
// GetConstructorFor prompts on the console; answer "y" (delete those types).
|
||||
Console.SetIn(new StringReader("y\n"));
|
||||
|
||||
loaded = new TypeTablePersistence();
|
||||
loaded.DeserializeIndexes(dir, null);
|
||||
|
||||
Assert.Single(loaded.EntitiesBySerial);
|
||||
Assert.True(loaded.EntitiesBySerial.ContainsKey((Serial)1u));
|
||||
Assert.True(loaded.TryGetTypeIndex(typeof(RoundTripEntity), out _));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.SetIn(previousIn);
|
||||
loaded?.Unregister();
|
||||
AssemblyHandler.Assemblies = previousAssemblies;
|
||||
Directory.Delete(dir, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LegacyV3IndexesStillLoadAndHydrateTheTypeTable()
|
||||
{
|
||||
var previousAssemblies = AssemblyHandler.Assemblies;
|
||||
AssemblyHandler.Assemblies = [.. previousAssemblies ?? [], typeof(RoundTripEntity).Assembly];
|
||||
|
||||
var dir = Path.Combine(Path.GetTempPath(), $"muo-legacyidx-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(Path.Combine(dir, "TypeTable"));
|
||||
|
||||
TypeTablePersistence loaded = null;
|
||||
|
||||
try
|
||||
{
|
||||
var hash = AssemblyHandler.GetTypeHash(typeof(RoundTripEntity));
|
||||
|
||||
// Hand-write a v3 idx: records carry flag + 8-byte hash, resolved via typesDb.
|
||||
using (var idx = new FileBufferWriter(Path.Combine(dir, "TypeTable", "TypeTable.idx")))
|
||||
{
|
||||
idx.Write(3); // version
|
||||
idx.Write(1); // record count
|
||||
idx.Write((byte)2); // xxHash3 flag
|
||||
idx.Write(hash);
|
||||
idx.Write(1u); // serial
|
||||
idx.Write(DateTime.UtcNow.Ticks);
|
||||
idx.Write(0L); // position
|
||||
idx.Write(4); // length
|
||||
}
|
||||
|
||||
var typesDb = new Dictionary<ulong, string> { [hash] = typeof(RoundTripEntity).FullName };
|
||||
|
||||
loaded = new TypeTablePersistence();
|
||||
loaded.DeserializeIndexes(dir, typesDb);
|
||||
|
||||
Assert.Single(loaded.EntitiesBySerial);
|
||||
Assert.True(loaded.EntitiesBySerial.ContainsKey((Serial)1u));
|
||||
|
||||
// Legacy loads must hydrate the table so the NEXT save can write v4 indexes.
|
||||
Assert.True(loaded.TryGetTypeIndex(typeof(RoundTripEntity), out _));
|
||||
}
|
||||
finally
|
||||
{
|
||||
loaded?.Unregister();
|
||||
AssemblyHandler.Assemblies = previousAssemblies;
|
||||
Directory.Delete(dir, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue