From b93934f6b432bff62efc144cc6b6308d9df4ee6a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 29 Sep 2026 04:21:59 +0200 Subject: [PATCH] Pin the variant rules a mutant survived or that lost their only test Reader refusals (v1 header over a variant, payload on a variant, unknown op), a load that finds a variant flattened by another writer, a re-resolution reaching a read-only cache, a base removed in the same batch as its new variant, and plain-record-to-variant refused without the name rule masking it. Co-Authored-By: Claude Sonnet 5 --- tests/GameCult.Caching.Tests/VariantTests.cs | 99 +++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/tests/GameCult.Caching.Tests/VariantTests.cs b/tests/GameCult.Caching.Tests/VariantTests.cs index 501af538..fec33c96 100644 --- a/tests/GameCult.Caching.Tests/VariantTests.cs +++ b/tests/GameCult.Caching.Tests/VariantTests.cs @@ -304,6 +304,14 @@ namespace GameCult.Caching.Tests batch.Remove(spare); batch.Remove(BigKey); }), Is.True, "removed in the same batch"); + + SeedBase(cache); + var removedInTheBatch = Refused(() => cache.Commit(batch => + { + batch.Remove(BaseKey); + batch.UpsertVariant(BigKey, BaseKey); + })); + Assert.That(removedInTheBatch.Message, Does.Contain(BigKey.Value).And.Contain(BaseKey.Value)); } [Test] @@ -371,7 +379,9 @@ namespace GameCult.Caching.Tests Assert.That(Refused(() => cache.FlattenAsync(BaseKey).GetAwaiter().GetResult()).Message, Does.Contain(BaseKey.Value)); var spare = new CultRecordKey("spare"); cache.Commit(batch => batch.Upsert(typeof(VariantGear), new VariantGear { Name = "spare" }, spare)); - Assert.That(Refused(() => cache.UpsertVariantAsync(spare, BaseKey).GetAwaiter().GetResult()).Message, Does.Contain(spare.Value)); + var named = new[] { cache.Override(nameof(VariantGear.Name), "spare variant") }; + Assert.That(Refused(() => cache.UpsertVariantAsync(spare, BaseKey, named).GetAwaiter().GetResult()).Message, Does.Contain(spare.Value)); + Assert.That(cache.GetStored(spare)!.Variant, Is.Null, "the plain record is still plain"); } [Test] @@ -529,10 +539,16 @@ namespace GameCult.Caching.Tests SeedBig(cache); } - var before = File.ReadAllBytes(path); using var readOnly = Open(path, readOnly: true); Assert.That(readOnly.Get(BigKey)!.Power, Is.EqualTo(25)); Assert.That(readOnly.Get(BigKey)!.Code, Is.EqualTo("l1")); + + // A base edit another writer commits reaches the read-only cache's variant when it pulls: a re-resolution is no write. + using var writer = Open(path); + EditBase(writer, gear => gear.Tags.Add("pierce")); + var before = File.ReadAllBytes(path); + readOnly.PullAllBackingStoresAsync(); + Assert.That(readOnly.Get(BigKey)!.Tags, Is.EqualTo(new[] { "beam", "pierce" })); Assert.That(File.ReadAllBytes(path), Is.EqualTo(before)); } @@ -553,6 +569,24 @@ namespace GameCult.Caching.Tests "the pull carried only the base; the variant was re-resolved in the admitting hold"); } + [Test] + public void AFlattenFromAnotherWriterReachesACacheHoldingTheVariant() + { + var path = PathOf("gear.cc"); + using var a = Open(path); + SeedBase(a); + SeedBig(a); + using var b = Open(path); + Assert.That(b.GetStored(BigKey)!.Variant, Is.Not.Null); + + a.FlattenAsync(BigKey).GetAwaiter().GetResult(); + a.FlushAsync().GetAwaiter().GetResult(); + b.PullAllBackingStoresAsync(); + + Assert.That(b.GetStored(BigKey)!.Variant, Is.Null, "a load reports what is on disk; it is not a plain write refused at a variant key"); + Assert.That(b.Get(BigKey)!.Power, Is.EqualTo(25)); + } + [Test] public void ARebaseResolvesFromTheNewBase() { @@ -569,6 +603,67 @@ namespace GameCult.Caching.Tests Assert.That(cache.GetStored(BigKey)!.Variant!.BaseKey, Is.EqualTo(spare.Value)); } + // ---- readers ---- + + private string WriteStore(params CultPersistedRecord[] records) => WriteStore(CultPersistedStoreSnapshot.FormatV2, records); + + private string WriteStore(string formatVersion, params CultPersistedRecord[] records) + { + var path = PathOf($"hand-{Guid.NewGuid():N}.cc"); + var gear = Registry.GetRequired(); + File.WriteAllBytes(path, CultDocumentMessagePackSerialization.SerializeSnapshot(new CultPersistedStoreSnapshot + { + FormatVersion = formatVersion, + SchemaCatalog = new[] { gear.ToCatalogEntry() }, + Records = records + })); + return path; + } + + private static CultPersistedRecord PlainGear(string key) => new() + { + Key = key, + SchemaId = Registry.GetRequired().SchemaId, + StoredAt = "2026-01-01T00:00:00.0000000Z", + Payload = CultDocumentMessagePackSerialization.SerializeUntyped(Laser(), typeof(VariantGear), Registry) + }; + + private static CultPersistedRecord VariantGearRecord(string key, string baseKey, byte[]? payload = null, CultOverrideOp op = CultOverrideOp.Set) => new() + { + Key = key, + SchemaId = Registry.GetRequired().SchemaId, + StoredAt = "2026-01-01T00:00:00.0000000Z", + Payload = payload ?? Array.Empty(), + Variant = new CultVariantDelta(baseKey, new[] + { + new CultVariantOverride(op, new[] { new CultPathStep(0) }, string.Empty, new byte[] { 0xa1, (byte)'x' }) + }) + }; + + [Test] + public void AVariantRecordUnderAV1HeaderIsRefusedNamingTheRecord() + { + var path = WriteStore(CultPersistedStoreSnapshot.FormatV1, PlainGear("hand-base"), VariantGearRecord("hand-variant", "hand-base")); + var error = Assert.Throws(() => Open(path).Dispose())!; + Assert.That(error.Message, Does.Contain("hand-variant").And.Contain("cultcache.store.v2")); + } + + [Test] + public void AVariantRecordWithAPayloadIsRefusedNamingTheRecord() + { + var path = WriteStore(PlainGear("hand-base"), VariantGearRecord("hand-variant", "hand-base", payload: new byte[] { 0x90 })); + var error = Assert.Throws(() => Open(path).Dispose())!; + Assert.That(error.Message, Does.Contain("hand-variant").And.Contain("payload")); + } + + [Test] + public void AnOverrideWithAnUnknownOpIsRefusedNamingTheRecord() + { + var path = WriteStore(PlainGear("hand-base"), VariantGearRecord("hand-variant", "hand-base", op: (CultOverrideOp)7)); + var error = Assert.Throws(() => Open(path).Dispose())!; + Assert.That(error.Message, Does.Contain("hand-variant").And.Contain("op 7")); + } + // ---- the other stores refuse loudly until their own cuts ---- [Test]