CachelessSingleFileTests: a call keeps its registry while another call waits at the gate

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-23 01:46:09 +02:00
commit 7d25d92bd7

View file

@ -60,6 +60,8 @@ namespace GameCult.Caching.Tests
private int _mostInside;
public readonly ManualResetEventSlim Entered = new();
public readonly ManualResetEventSlim Release = new();
public CultDocumentRegistry? RegistryBeforeWait;
public CultDocumentRegistry? RegistryAfterWait;
public GatedStore(string path) : base(path) { }
@ -83,7 +85,11 @@ namespace GameCult.Caching.Tests
try
{
if (Interlocked.Exchange(ref _held, 1) == 0)
{
RegistryBeforeWait = Registry;
Release.Wait(TimeSpan.FromSeconds(30));
RegistryAfterWait = Registry;
}
var store = CultDocumentMessagePackSerialization.ReadStore(data);
CultDocumentMessagePackSerialization.RequireSingleFileFormat(store.Snapshot);
return store;
@ -121,6 +127,28 @@ namespace GameCult.Caching.Tests
Assert.That(second.Result!.Records, Has.Length.EqualTo(1));
}
// A call's registry is attached under the store's gate: a second call with another registry waits for the first, so the
// first sees its own registry from its start to its end.
[Test]
public async Task ACallKeepsItsRegistryWhileAnotherCallWaits()
{
var mine = CultDocumentRegistry.ForTypes(new[] { typeof(IdDeck) });
var other = CultDocumentRegistry.ForTypes(new[] { typeof(DeclaredBase) });
new SingleFileMessagePackBackingStore(_path).WriteRecord(Record("a", One, 1), One);
var store = new GatedStore(_path);
var first = Task.Run(() => store.WriteDocument(new CultRecordKey("first"), new IdDeck { Name = "first" }, registry: mine));
Assert.That(store.Entered.Wait(TimeSpan.FromSeconds(10)), Is.True, "the first call reached the reader");
var second = Task.Run(() => store.WriteDocument(new CultRecordKey("second"), new DeclaredBase { Name = "second" }, registry: other));
await Task.Delay(500);
store.Release.Set();
await Task.WhenAll(first, second);
Assert.That(store.RegistryBeforeWait, Is.SameAs(mine));
Assert.That(store.RegistryAfterWait, Is.SameAs(mine), "the second call's registry did not replace the first's mid-call");
Assert.That(new SingleFileMessagePackBackingStore(_path).ReadStoreSnapshot()!.Records.Select(record => record.Key), Is.EquivalentTo(new[] { "a", "first", "second" }));
}
// WriteDocument<T> stores the document as T: the schema id, the id plan, the header and the bytes all come from the one
// declared type, as they did when CultMesh encoded typeof(T).
[Test]