C#: a blank declared compatible schema id is dropped, not refused

A type that failed to build a descriptor poisoned the shared registry's discovery of every
loaded document. A blank id is now filtered like the type's own id, and the test declares one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-30 11:31:28 +02:00
commit d589e61f67
2 changed files with 3 additions and 19 deletions

View file

@ -790,10 +790,8 @@ namespace GameCult.Caching
var contentHash = Sha256(schemaJson);
var semanticFingerprint = BuildSemanticFingerprint(attribute.SchemaName, attribute.SchemaVersion, descriptorMembers);
var schemaId = Sha256(semanticFingerprint);
if (attribute.CompatibleSchemaIds.Any(string.IsNullOrWhiteSpace))
throw new InvalidOperationException($"Type {type.FullName} declares an empty compatible schema id.");
var compatibleSchemaIds = attribute.CompatibleSchemaIds
.Where(id => !string.Equals(id, schemaId, StringComparison.Ordinal))
.Where(id => !string.IsNullOrWhiteSpace(id) && !string.Equals(id, schemaId, StringComparison.Ordinal))
.Distinct(StringComparer.Ordinal)
.ToArray();

View file

@ -32,7 +32,7 @@ namespace GameCult.Caching.Tests
[Key(0)] [CultName] public string Name { get; set; } = "";
}
[CultDocument("tests.compat_owner", "tests.compat_owner.v1", CompatibleSchemaIds = new[] { OwnerId })]
[CultDocument("tests.compat_owner", "tests.compat_owner.v1", CompatibleSchemaIds = new[] { OwnerId, " " })]
[MessagePackObject]
public sealed class Owner
{
@ -53,13 +53,6 @@ namespace GameCult.Caching.Tests
[Key(0)] [CultName] public string Name { get; set; } = "";
}
[CultDocument("tests.compat_empty", "tests.compat_empty.v1", CompatibleSchemaIds = new[] { " " })]
[MessagePackObject]
public sealed class EmptyClaim
{
[Key(0)] [CultName] public string Name { get; set; } = "";
}
private static readonly CultDocumentRegistry Declaring = CultDocumentRegistry.ForTypes(new[] { typeof(DeclaringDeck) });
private static readonly CultDocumentRegistry Bare = CultDocumentRegistry.ForTypes(new[] { typeof(BareDeck) });
private static readonly CultRecordKey D = new("d");
@ -232,7 +225,7 @@ namespace GameCult.Caching.Tests
var descriptor = registry.GetRequired<Owner>();
Assert.That(descriptor.SchemaId, Is.EqualTo(OwnerId), "the pinned id is this schema's id");
Assert.That(descriptor.CompatibleSchemaIds, Is.Empty, "a type's own id is not a declared compatible id");
Assert.That(descriptor.CompatibleSchemaIds, Is.Empty, "a type's own id and a blank id are not declared compatible ids");
Assert.That(descriptor.ToCatalogEntry().CompatibleSchemaIds, Is.EqualTo(new[] { OwnerId }));
}
@ -261,12 +254,5 @@ namespace GameCult.Caching.Tests
{
Assert.That(Declaring.CanonicalSchemaId("nobody.id"), Is.EqualTo("nobody.id"));
}
[Test]
public void AnEmptyDeclaredIdIsRefused()
{
Assert.That(() => CultDocumentRegistry.ForTypes(new[] { typeof(EmptyClaim) }).GetRequired<EmptyClaim>(),
Throws.InvalidOperationException.With.Message.Contains("empty compatible schema id"));
}
}
}