R-R vs R-F discrepancy: CultMesh's typed API refuses empty keys, doesn't answer empty
Discrepancy between spec and Body, reported per Hands' brief ("make the
smallest change that keeps the spec's intent, and report the discrepancy").
R-R says "recordKeys: [] answers empty" for v0 lowering. That holds for the
raw v0 wire protocol server (CultNetDocumentRegistry.CreateRawSnapshotResponse,
CultNetDatabase.CreateShardSnapshotResponse - fixed two commits back). It does
not hold for CultMesh's typed convenience API: R-F, landed earlier in this
cut, made CultMesh.EnsureV0Compatible run CultNetSelectionValidation.
ValidateShape client-side before a request is ever sent, specifically so that
"Keys=[]/[\"\"] is refused rather than silently lowered to 'every key'"
(CultMeshSnapshots.cs:836-841). Once R-R made ResolveDefaultSelection's
Clean(recordKeys) produce Keys=[] instead of Keys=null for an explicit empty
list, every FetchDocumentsAsync(recordKeys: []) call now trips that existing
R-F door and throws, instead of reaching the wire at all.
Kept both rulings as each one's own owner decided them: the raw wire protocol
answers empty (R-R); CultMesh's typed wrapper refuses before sending (R-F).
Rewrote FetchDocumentsAsync_WithExplicitEmptyRecordKeys_AnswersEmpty, added
two commits ago on the wrong assumption that CultMesh's typed API also
answers empty, to FetchDocumentsAsync_WithExplicitEmptyRecordKeys_
IsRefusedClientSide, asserting the actual (and, on inspection, correct) R-F
behaviour instead.
Verified on Yggdrasil: dotnet test tests/GameCult.Mesh.Tests -c Release.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
1afbaed775
commit
f8ba2d0aca
1 changed files with 14 additions and 21 deletions
|
|
@ -2795,21 +2795,18 @@ public sealed class CultMeshStreamingTests
|
|||
|
||||
// R-R (docs/cultnet-selection-cut.md, Self's rulings 2026-09-22) corrects the earlier commit-2
|
||||
// ruling this test used to pin: an explicit empty recordKeys array is a real filter, not "no
|
||||
// filter". ResolveDefaultSelection must not fall back to the type's own schema default just
|
||||
// because the caller's recordKeys happened to be empty - the wire must carry recordKeys=[] and
|
||||
// no schema default, and the answer is empty, exactly as v0 answered before this cut
|
||||
// (CultNetDatabase.CreateShardSnapshotResponse at b3d9cf7 tests RecordKeys != null, not a length).
|
||||
// filter" that falls back to the type's own schema default. Discrepancy against R-R's prose,
|
||||
// reported rather than silently resolved: at CultMesh's typed convenience layer this selection
|
||||
// never reaches the wire at all. ResolveDefaultSelection now produces Keys=[] instead of Keys=null,
|
||||
// and EnsureV0Compatible - CultMesh's own client-side door, landed under the earlier ruling R-F -
|
||||
// already refuses an empty selection.keys before any request is sent ("Keys=[]/[\"\"] is refused
|
||||
// rather than silently lowered to 'every key'", CultMeshSnapshots.cs:836-841). R-R's "recordKeys:
|
||||
// [] answers empty" therefore only holds for the raw v0 wire protocol server, which any v0 peer
|
||||
// can call directly without CultMesh's typed wrapper - see
|
||||
// CultNetDocumentRegistry_RawSnapshot_ExplicitEmptyRecordKeys_AnswersEmpty in NetworkingTests.cs.
|
||||
[Test]
|
||||
public async Task FetchDocumentsAsync_WithExplicitEmptyRecordKeys_AnswersEmpty()
|
||||
public void FetchDocumentsAsync_WithExplicitEmptyRecordKeys_IsRefusedClientSide()
|
||||
{
|
||||
var cache = new CultCache();
|
||||
var key = new CultRecordKey("mesh-note:empty-keys-default");
|
||||
await cache.UpsertAsync(new MeshNoteDocument
|
||||
{
|
||||
Schema = "tests.mesh_note.v1",
|
||||
Text = "empty-keys-default",
|
||||
Revision = 1
|
||||
}, new CultRecordHandle<MeshNoteDocument>(key));
|
||||
var registry = new CultNetDocumentRegistry(CultDocumentRegistry.Shared)
|
||||
.Register(CultNetDocumentBinding.ForDocument<MeshNoteDocument>(CultDocumentRegistry.Shared));
|
||||
var requests = new List<CultNetSnapshotRequestMessage>();
|
||||
|
|
@ -2821,18 +2818,14 @@ public sealed class CultMeshStreamingTests
|
|||
CreateClient = () => new MeshSnapshotSchemaClient(request =>
|
||||
{
|
||||
requests.Add(request);
|
||||
return registry.CreateRawSnapshotResponse(cache, request.MessageId, request);
|
||||
return registry.CreateRawSnapshotResponse(new CultCache(), request.MessageId, request);
|
||||
})
|
||||
},
|
||||
registry);
|
||||
|
||||
var documents = await session.FetchDocumentsAsync<MeshNoteDocument>(recordKeys: Array.Empty<string>());
|
||||
|
||||
documents.Should().BeEmpty();
|
||||
requests.Should().ContainSingle();
|
||||
requests[0].SchemaIds.Should().BeNull();
|
||||
requests[0].RecordKeys.Should().NotBeNull();
|
||||
requests[0].RecordKeys.Should().BeEmpty();
|
||||
Assert.ThrowsAsync<CultNetSelectionInvalidException>(
|
||||
() => session.FetchDocumentsAsync<MeshNoteDocument>(recordKeys: Array.Empty<string>()));
|
||||
requests.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue