2026-07-13 17:16:41 +01:00
|
|
|
using System;
|
2026-07-13 17:22:58 +01:00
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
2026-07-13 17:16:41 +01:00
|
|
|
using NUnit.Framework;
|
2026-09-30 12:35:52 +02:00
|
|
|
using static GameCult.Caching.Tests.EmittedDocumentTypes;
|
2026-07-13 17:16:41 +01:00
|
|
|
|
|
|
|
|
namespace GameCult.Caching.Tests;
|
|
|
|
|
|
|
|
|
|
[TestFixture]
|
|
|
|
|
public sealed class CultDocumentRegistryTests
|
|
|
|
|
{
|
2026-07-20 01:48:56 +01:00
|
|
|
[Test]
|
|
|
|
|
public void ForTypesContainsOnlyExplicitDocuments()
|
|
|
|
|
{
|
2026-09-30 12:35:52 +02:00
|
|
|
var requested = Emit("explicit", "tests.registry.explicit", "v1");
|
|
|
|
|
var unrelated = Emit("unrelated", "tests.registry.unrelated", "v1");
|
2026-07-20 01:48:56 +01:00
|
|
|
|
|
|
|
|
var registry = CultDocumentRegistry.ForTypes(new[] { requested, requested });
|
|
|
|
|
|
|
|
|
|
Assert.That(registry.AllDescriptors.Select(value => value.DocumentType), Is.EqualTo(new[] { requested }));
|
|
|
|
|
Assert.That(registry.AllDescriptors.Any(value => value.DocumentType == unrelated), Is.False);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 17:16:41 +01:00
|
|
|
[Test]
|
|
|
|
|
public void IdenticalTypeRegistrationIsIdempotent()
|
|
|
|
|
{
|
|
|
|
|
var registry = new CultDocumentRegistry();
|
2026-09-30 12:35:52 +02:00
|
|
|
var type = Emit("idempotent", "tests.registry.idempotent", "v1");
|
2026-07-13 17:16:41 +01:00
|
|
|
|
|
|
|
|
var first = registry.GetRequired(type);
|
|
|
|
|
var second = registry.GetRequired(type);
|
|
|
|
|
|
|
|
|
|
Assert.That(second, Is.SameAs(first));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void DifferentVersionsSharingSchemaNameRegisterWithoutReflectionOrderSelection()
|
|
|
|
|
{
|
|
|
|
|
var registry = new CultDocumentRegistry();
|
2026-09-30 12:35:52 +02:00
|
|
|
var firstType = Emit("version_one", "tests.registry.versioned", "v1");
|
|
|
|
|
var secondType = Emit("version_two", "tests.registry.versioned", "v2");
|
2026-07-13 17:16:41 +01:00
|
|
|
|
|
|
|
|
var first = registry.GetRequired(firstType);
|
|
|
|
|
var second = registry.GetRequired(secondType);
|
|
|
|
|
|
|
|
|
|
Assert.Multiple(() =>
|
|
|
|
|
{
|
|
|
|
|
Assert.That(registry.GetRequiredBySchemaId(first.SchemaId), Is.SameAs(first));
|
|
|
|
|
Assert.That(registry.GetRequiredBySchemaId(second.SchemaId), Is.SameAs(second));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var persisted = new CultSchemaCatalogEntry
|
|
|
|
|
{
|
|
|
|
|
SchemaId = "tests.registry.versioned.unknown",
|
|
|
|
|
SchemaName = "tests.registry.versioned",
|
|
|
|
|
SchemaVersion = "legacy"
|
|
|
|
|
};
|
|
|
|
|
Assert.That(
|
|
|
|
|
() => registry.ResolvePersistedSchema(persisted.SchemaId, new[] { persisted }),
|
|
|
|
|
Throws.TypeOf<InvalidOperationException>()
|
|
|
|
|
.With.Message.Contains("ambiguous across local versions")
|
|
|
|
|
.And.Message.Contains("v1")
|
|
|
|
|
.And.Message.Contains("v2"));
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 17:22:58 +01:00
|
|
|
[Test]
|
|
|
|
|
public void ConcurrentRefreshAndReadsObserveCompleteRegistrySnapshots()
|
|
|
|
|
{
|
|
|
|
|
var registry = new CultDocumentRegistry();
|
2026-09-30 12:35:52 +02:00
|
|
|
var type = Emit("refresh_visible", "tests.registry.refresh_visible", "v1");
|
2026-07-13 17:22:58 +01:00
|
|
|
var descriptor = registry.GetRequired(type);
|
|
|
|
|
|
|
|
|
|
Parallel.For(0, 20, _ =>
|
|
|
|
|
{
|
|
|
|
|
registry.Refresh();
|
|
|
|
|
Assert.That(registry.GetRequired(type).SchemaId, Is.EqualTo(descriptor.SchemaId));
|
|
|
|
|
Assert.That(registry.GetRequiredBySchemaId(descriptor.SchemaId).SchemaId, Is.EqualTo(descriptor.SchemaId));
|
|
|
|
|
});
|
2026-07-13 17:16:41 +01:00
|
|
|
}
|
|
|
|
|
|
CultCache: a second type for one schema is refused, typed
F7 (operator, 2026-09-30): "Schema aliasing sounds like a really bad idea,
I support retiring it." C# now holds one type per schema, as TypeScript,
Python and Rust already do.
- Delete IsExactWireAlias and the branch that registered an identical second
type by CLR type only. A second type with the same schema name and version
is refused with CultSchemaConflictException naming both types and the
claimed id, whether it is identical, declares other compatible ids, or has
other members. The untyped DuplicateSchemaRegistration refusal and the
separate SchemaIdClaimedTwice path collapse into that one refusal.
- Owner-beats-lister (batch 10) is unchanged: one type owns an id, others may
list it as compatible.
- The per-name version index stays; it serves v1/v2 of one schema name, not
aliasing.
- Tests: the alias-registration tests are replaced by one refusal test over
identical, re-declared and re-shaped second types, in both registration
orders.
- cultcache-store-composition.md drops the AsSchemaAlias stale-protection note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:15:57 +02:00
|
|
|
// A second type for one schema is refused, typed, naming both and the claimed id, and the registry keeps what it had.
|
|
|
|
|
private static void AssertSecondClaimRefused(Type first, Type second)
|
2026-07-13 17:16:41 +01:00
|
|
|
{
|
CultCache: a second type for one schema is refused, typed
F7 (operator, 2026-09-30): "Schema aliasing sounds like a really bad idea,
I support retiring it." C# now holds one type per schema, as TypeScript,
Python and Rust already do.
- Delete IsExactWireAlias and the branch that registered an identical second
type by CLR type only. A second type with the same schema name and version
is refused with CultSchemaConflictException naming both types and the
claimed id, whether it is identical, declares other compatible ids, or has
other members. The untyped DuplicateSchemaRegistration refusal and the
separate SchemaIdClaimedTwice path collapse into that one refusal.
- Owner-beats-lister (batch 10) is unchanged: one type owns an id, others may
list it as compatible.
- The per-name version index stays; it serves v1/v2 of one schema name, not
aliasing.
- Tests: the alias-registration tests are replaced by one refusal test over
identical, re-declared and re-shaped second types, in both registration
orders.
- cultcache-store-composition.md drops the AsSchemaAlias stale-protection note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:15:57 +02:00
|
|
|
var schemaId = CultDocumentRegistry.ForTypes(new[] { second }).GetRequired(second).SchemaId;
|
2026-09-30 12:35:52 +02:00
|
|
|
var refusal = Assert.Throws<CultSchemaConflictException>(() => CultDocumentRegistry.ForTypes(new[] { first, second }))!;
|
|
|
|
|
Assert.Multiple(() =>
|
2026-07-13 17:16:41 +01:00
|
|
|
{
|
2026-09-30 12:35:52 +02:00
|
|
|
Assert.That(refusal.SchemaId, Is.EqualTo(schemaId));
|
|
|
|
|
Assert.That(refusal.Message, Does.Contain(first.FullName).And.Contain(second.FullName));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var registry = CultDocumentRegistry.ForTypes(new[] { first });
|
|
|
|
|
Assert.That(() => registry.GetRequired(second), Throws.TypeOf<CultSchemaConflictException>());
|
|
|
|
|
Assert.That(registry.AllDescriptors.Select(descriptor => descriptor.DocumentType), Is.EqualTo(new[] { first }));
|
|
|
|
|
}
|
|
|
|
|
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
private static CultSchemaCatalogEntry[] CatalogOf(params Type[] types) =>
|
|
|
|
|
types.Select(type => CultDocumentRegistry.ForTypes(new[] { type }).GetRequired(type).ToCatalogEntry()).ToArray();
|
|
|
|
|
|
|
|
|
|
// Owner beats lister: a type that owns an id and one that lists it register together in either order (the v1 class kept
|
|
|
|
|
// beside a v2 class declaring v1's id). A record under the id resolves to its owner; one under the lister's own id to the
|
|
|
|
|
// lister; without the owner registered, the id resolves to the lister.
|
2026-09-30 12:35:52 +02:00
|
|
|
[TestCase(false)]
|
|
|
|
|
[TestCase(true)]
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
public void AnOwnerAndATypeListingItsIdRegisterTogetherAndTheOwnerWins(bool reversed)
|
2026-09-30 12:35:52 +02:00
|
|
|
{
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
var owner = Emit("owns", "tests.registry.owns", "v1");
|
|
|
|
|
var ownerId = CultDocumentRegistry.ForTypes(new[] { owner }).GetRequired(owner).SchemaId;
|
|
|
|
|
var lister = Emit("lists", "tests.registry.owns", "v2", new[] { new Field("Extra", typeof(string), 0) }, new[] { ownerId });
|
|
|
|
|
var listerId = CultDocumentRegistry.ForTypes(new[] { lister }).GetRequired(lister).SchemaId;
|
|
|
|
|
var catalog = CatalogOf(owner, lister);
|
2026-09-30 12:35:52 +02:00
|
|
|
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
var registry = CultDocumentRegistry.ForTypes(reversed ? new[] { lister, owner } : new[] { owner, lister });
|
|
|
|
|
|
|
|
|
|
Assert.Multiple(() =>
|
|
|
|
|
{
|
|
|
|
|
Assert.That(registry.AllDescriptors.Select(descriptor => descriptor.DocumentType), Is.EquivalentTo(new[] { owner, lister }));
|
|
|
|
|
Assert.That(registry.ResolvePersistedSchema(ownerId, catalog).DocumentType, Is.EqualTo(owner));
|
|
|
|
|
Assert.That(registry.ResolvePersistedSchema(listerId, catalog).DocumentType, Is.EqualTo(lister));
|
|
|
|
|
Assert.That(CultDocumentRegistry.ForTypes(new[] { lister }).ResolvePersistedSchema(ownerId, catalog).DocumentType, Is.EqualTo(lister));
|
|
|
|
|
});
|
2026-09-30 12:35:52 +02:00
|
|
|
}
|
|
|
|
|
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
// Two types listing one id register together in either order. With an owner registered, a record under the id is the
|
|
|
|
|
// owner's; with none, the id names no single type, and a record under it is refused, typed, naming the listers.
|
2026-09-30 12:35:52 +02:00
|
|
|
[TestCase(false)]
|
|
|
|
|
[TestCase(true)]
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
public void TwoTypesListingOneIdRegisterAndARecordUnderItNeedsAnOwner(bool reversed)
|
2026-09-30 12:35:52 +02:00
|
|
|
{
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
var owner = Emit("shared_owner", "tests.registry.shared", "v1");
|
|
|
|
|
var sharedId = CultDocumentRegistry.ForTypes(new[] { owner }).GetRequired(owner).SchemaId;
|
|
|
|
|
var a = Emit("declares_a", "tests.registry.declares_a", "v1", compatibleSchemaIds: new[] { sharedId });
|
|
|
|
|
var b = Emit("declares_b", "tests.registry.declares_b", "v1", compatibleSchemaIds: new[] { sharedId });
|
|
|
|
|
var listers = reversed ? new[] { b, a } : new[] { a, b };
|
|
|
|
|
var catalog = CatalogOf(owner);
|
|
|
|
|
|
|
|
|
|
var ambiguous = CultDocumentRegistry.ForTypes(listers);
|
|
|
|
|
var refusal = Assert.Throws<CultSchemaConflictException>(() => ambiguous.ResolvePersistedSchema(sharedId, catalog))!;
|
|
|
|
|
Assert.Multiple(() =>
|
|
|
|
|
{
|
|
|
|
|
Assert.That(refusal.SchemaId, Is.EqualTo(sharedId));
|
|
|
|
|
Assert.That(refusal.SchemaNames, Is.EquivalentTo(new[] { "tests.registry.declares_a", "tests.registry.declares_b" }));
|
|
|
|
|
Assert.That(refusal.Message, Does.Contain(a.FullName).And.Contain(b.FullName));
|
|
|
|
|
});
|
2026-09-30 12:35:52 +02:00
|
|
|
|
C#: a type listing an id registers beside the type that owns it; the owner wins
Batch 9 refused any overlap between two types' ids, which forbade the migration
CompatibleSchemaIds exists for: v2 declares v1's id while v1's class stays
registered (Soul F8). The rule is now owner beats lister. A type owns its own
schema id; any number of types may list an id as compatible beside its owner,
registering in either order. A record under an id resolves to its owner, else to
the one type listing it. An id no type owns and several list names no single
type, so a record under it is refused, typed (CultSchemaConflictException naming
the id and the listers), where it resolves: refusing at registration would
depend on whether the owner had registered yet.
A second owner of one id is still refused at registration, typed. A schema id
hashes the schema's name and version, so the only second owner is a type of the
same name and version, which the name-and-version check already meets; the
separate own-id lookup could never fire and is gone.
Tests: an owner and a lister register in both orders, the owner's id resolves to
the owner, the lister's own id to the lister, and without the owner to the
lister; two listers register in both orders, a record under their id is refused
typed with no owner and resolves to the owner with one, registered before or
after them; a cache with v1 beside v2 reads each version's records as its own
type, in both registration orders. The emitted-fixture note says why a fixture
is emitted now that v1/v2 pairs no longer need hiding from the shared registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:26:06 +02:00
|
|
|
Assert.That(CultDocumentRegistry.ForTypes(listers.Append(owner)).ResolvePersistedSchema(sharedId, catalog).DocumentType, Is.EqualTo(owner));
|
|
|
|
|
Assert.That(CultDocumentRegistry.ForTypes(listers.Prepend(owner)).ResolvePersistedSchema(sharedId, catalog).DocumentType, Is.EqualTo(owner));
|
2026-09-30 12:35:52 +02:00
|
|
|
}
|
|
|
|
|
|
CultCache: a second type for one schema is refused, typed
F7 (operator, 2026-09-30): "Schema aliasing sounds like a really bad idea,
I support retiring it." C# now holds one type per schema, as TypeScript,
Python and Rust already do.
- Delete IsExactWireAlias and the branch that registered an identical second
type by CLR type only. A second type with the same schema name and version
is refused with CultSchemaConflictException naming both types and the
claimed id, whether it is identical, declares other compatible ids, or has
other members. The untyped DuplicateSchemaRegistration refusal and the
separate SchemaIdClaimedTwice path collapse into that one refusal.
- Owner-beats-lister (batch 10) is unchanged: one type owns an id, others may
list it as compatible.
- The per-name version index stays; it serves v1/v2 of one schema name, not
aliasing.
- Tests: the alias-registration tests are replaced by one refusal test over
identical, re-declared and re-shaped second types, in both registration
orders.
- cultcache-store-composition.md drops the AsSchemaAlias stale-protection note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:15:57 +02:00
|
|
|
// One type per schema, in every runtime: a second type of the same schema name and version is refused whichever registers
|
|
|
|
|
// first, whether it is identical to the first, declares different compatible ids, or has different members.
|
|
|
|
|
[TestCase("identical", false)]
|
|
|
|
|
[TestCase("identical", true)]
|
|
|
|
|
[TestCase("declaration", false)]
|
|
|
|
|
[TestCase("declaration", true)]
|
|
|
|
|
[TestCase("members", false)]
|
|
|
|
|
[TestCase("members", true)]
|
|
|
|
|
public void ASecondTypeForOneSchemaIsRefused(string difference, bool reversed)
|
2026-09-30 12:35:52 +02:00
|
|
|
{
|
CultCache: a second type for one schema is refused, typed
F7 (operator, 2026-09-30): "Schema aliasing sounds like a really bad idea,
I support retiring it." C# now holds one type per schema, as TypeScript,
Python and Rust already do.
- Delete IsExactWireAlias and the branch that registered an identical second
type by CLR type only. A second type with the same schema name and version
is refused with CultSchemaConflictException naming both types and the
claimed id, whether it is identical, declares other compatible ids, or has
other members. The untyped DuplicateSchemaRegistration refusal and the
separate SchemaIdClaimedTwice path collapse into that one refusal.
- Owner-beats-lister (batch 10) is unchanged: one type owns an id, others may
list it as compatible.
- The per-name version index stays; it serves v1/v2 of one schema name, not
aliasing.
- Tests: the alias-registration tests are replaced by one refusal test over
identical, re-declared and re-shaped second types, in both registration
orders.
- cultcache-store-composition.md drops the AsSchemaAlias stale-protection note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:15:57 +02:00
|
|
|
var schemaName = $"tests.registry.claimed_twice_{difference}";
|
|
|
|
|
var first = Emit($"first_{difference}", schemaName, "v1");
|
|
|
|
|
var second = difference switch
|
|
|
|
|
{
|
|
|
|
|
"identical" => Emit($"second_{difference}", schemaName, "v1"),
|
|
|
|
|
"declaration" => Emit($"second_{difference}", schemaName, "v1", compatibleSchemaIds: new[] { "tests.registry.older" }),
|
|
|
|
|
_ => Emit($"second_{difference}", schemaName, "v1", new[] { new Field("Value", typeof(string), 0) }),
|
|
|
|
|
};
|
2026-07-13 17:16:41 +01:00
|
|
|
|
CultCache: a second type for one schema is refused, typed
F7 (operator, 2026-09-30): "Schema aliasing sounds like a really bad idea,
I support retiring it." C# now holds one type per schema, as TypeScript,
Python and Rust already do.
- Delete IsExactWireAlias and the branch that registered an identical second
type by CLR type only. A second type with the same schema name and version
is refused with CultSchemaConflictException naming both types and the
claimed id, whether it is identical, declares other compatible ids, or has
other members. The untyped DuplicateSchemaRegistration refusal and the
separate SchemaIdClaimedTwice path collapse into that one refusal.
- Owner-beats-lister (batch 10) is unchanged: one type owns an id, others may
list it as compatible.
- The per-name version index stays; it serves v1/v2 of one schema name, not
aliasing.
- Tests: the alias-registration tests are replaced by one refusal test over
identical, re-declared and re-shaped second types, in both registration
orders.
- cultcache-store-composition.md drops the AsSchemaAlias stale-protection note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:15:57 +02:00
|
|
|
AssertSecondClaimRefused(reversed ? second : first, reversed ? first : second);
|
2026-07-13 17:16:41 +01:00
|
|
|
}
|
|
|
|
|
}
|