From ef5cd881119d6a16d5338c9cce09bf654ad46ed7 Mon Sep 17 00:00:00 2001 From: Libby Date: Sun, 8 Nov 2026 15:13:58 +0000 Subject: [PATCH] C#: v0 errors for snapshot, shard log and variant refusals carry fixed text, never an exception's message Co-Authored-By: Claude Sonnet 5.5 --- .../CultNetDatabaseServer.cs | 7 ++-- .../CultNetDocumentRegistryVariantTests.cs | 1 + .../CultNetWriteReceiptServerTests.cs | 32 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/GameCult.Networking/CultNetDatabaseServer.cs b/src/GameCult.Networking/CultNetDatabaseServer.cs index 32688434..16967d49 100644 --- a/src/GameCult.Networking/CultNetDatabaseServer.cs +++ b/src/GameCult.Networking/CultNetDatabaseServer.cs @@ -266,7 +266,7 @@ namespace GameCult.Networking catch (Exception ex) { _server.Logger.LogError($"CultNet snapshot v1 request failed: {ex.Message}"); - peer.SendCultNet(new CultNetErrorMessage { Error = ex.Message }); + peer.SendCultNet(new CultNetErrorMessage { Error = "the snapshot request could not be answered" }); } return Task.CompletedTask; @@ -286,7 +286,8 @@ namespace GameCult.Networking } catch (Exception ex) { - peer.SendCultNet(new CultNetErrorMessage { Error = ex.Message }); + _server.Logger.LogError($"CultNet shard log request failed: {ex.Message}"); + peer.SendCultNet(new CultNetErrorMessage { Error = "the shard log request could not be answered" }); } return Task.CompletedTask; @@ -754,7 +755,7 @@ namespace GameCult.Networking var refusedKey = change.GetType().GetProperty("Key")?.GetValue(change) is CultRecordKey changed ? changed.Value : string.Empty; lock (refused) refused.Add(refusedKey); - sendError(new CultNetErrorMessage { Error = refusal.Message }); + sendError(new CultNetErrorMessage { Error = "the record is a variant CultNet cannot carry" }); return null; } diff --git a/tests/GameCult.Networking.Tests/CultNetDocumentRegistryVariantTests.cs b/tests/GameCult.Networking.Tests/CultNetDocumentRegistryVariantTests.cs index 64de1093..5713b0da 100644 --- a/tests/GameCult.Networking.Tests/CultNetDocumentRegistryVariantTests.cs +++ b/tests/GameCult.Networking.Tests/CultNetDocumentRegistryVariantTests.cs @@ -418,6 +418,7 @@ namespace GameCult.Networking.Tests CultNetDatabaseChangeKind.Added, VariantKey, schemaId, database.Shards[0], cache.Get(VariantKey)!, previousDocument: null); Assert.That(Deliver(databaseServer, added, selection, refused, errors), Is.Null); Assert.That(errors, Has.Count.EqualTo(1)); + Assert.That(errors[0].Error, Is.EqualTo("the record is a variant CultNet cannot carry")); var removed = new CultNetDatabaseChange( CultNetDatabaseChangeKind.Removed, VariantKey, schemaId, database.Shards[0], document: null, previousDocument: cache.Get(VariantKey)!); diff --git a/tests/GameCult.Networking.Tests/CultNetWriteReceiptServerTests.cs b/tests/GameCult.Networking.Tests/CultNetWriteReceiptServerTests.cs index eb20b345..09188c5f 100644 --- a/tests/GameCult.Networking.Tests/CultNetWriteReceiptServerTests.cs +++ b/tests/GameCult.Networking.Tests/CultNetWriteReceiptServerTests.cs @@ -1087,5 +1087,37 @@ namespace GameCult.Networking.Tests Assert.That(session.TryBegin("m1", HashOf("b"), out _), Is.EqualTo(CultNetWriteReceiptSession.Begin.New)); Assert.That(session.TryBegin("m1", HashOf("b"), out _), Is.EqualTo(CultNetWriteReceiptSession.Begin.Unanswered)); } + + // ---- Fixed text in v0 errors: an exception's own words never reach the wire ---- + + private static Func HandlerOf(Rig rig, string field) => + (Func)typeof(CultNetDatabaseServer) + .GetField(field, BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(rig.DatabaseServer)!; + + [Test] + public async Task ASnapshotFaultIsAnsweredWithFixedTextNotTheExceptionMessage() + { + var rig = Build(); + var wire = new Wire(); + + await HandlerOf(rig, "_snapshotHandlerV1")( + new CultNetSnapshotRequestV1Message { MessageId = "s1", Selection = null! }, wire.Peer); + + var error = (CultNetErrorMessage)wire.Sent.Single(); + Assert.That(error.Error, Is.EqualTo("the snapshot request could not be answered")); + } + + [Test] + public async Task AShardLogFaultIsAnsweredWithFixedTextNotTheExceptionMessage() + { + var rig = Build(); + var wire = new Wire(); + + await HandlerOf(rig, "_shardLogHandler")( + new CultNetShardLogRequestMessage { MessageId = "l1", ShardId = string.Empty }, wire.Peer); + + var error = (CultNetErrorMessage)wire.Sent.Single(); + Assert.That(error.Error, Is.EqualTo("the shard log request could not be answered")); + } } }