From 4f747d45674f0ea97d95834fd965fb4d5082b02b Mon Sep 17 00:00:00 2001 From: Libby Date: Thu, 12 Nov 2026 14:27:50 +0000 Subject: [PATCH] Networking tests: pin the write-side depth check on ToRawRecord with no binding Co-Authored-By: Claude Sonnet 5.5 --- .../BytesAfterPathTests.cs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/GameCult.Networking.Tests/BytesAfterPathTests.cs b/tests/GameCult.Networking.Tests/BytesAfterPathTests.cs index 6a56ceb1..0b125075 100644 --- a/tests/GameCult.Networking.Tests/BytesAfterPathTests.cs +++ b/tests/GameCult.Networking.Tests/BytesAfterPathTests.cs @@ -21,7 +21,15 @@ namespace GameCult.Networking.Tests [Key(1)] public int N = 3; } - private static readonly CultDocumentRegistry Documents = CultDocumentRegistry.ForTypes(new[] { typeof(TailDoc) }); + [CultDocument("tests.bytes_after_deep", "tests.bytes_after_deep.v1")] + [MessagePackObject] + public sealed class DeepDoc + { + [Key(0)] [CultName] public string Id = "a"; + [Key(1)] public DeepDoc Inner; + } + + private static readonly CultDocumentRegistry Documents = CultDocumentRegistry.ForTypes(new[] { typeof(TailDoc), typeof(DeepDoc) }); private static byte[] WithTail() => CultDocumentMessagePackSerialization.SerializeUntyped(new TailDoc(), typeof(TailDoc), Documents).Concat(new byte[] { 0xc0 }).ToArray(); @@ -58,5 +66,28 @@ namespace GameCult.Networking.Tests var registry = new CultNetDocumentRegistry(Documents); Refuses(() => registry.DeserializeRawDocument(Raw(registry, WithTail()))); } + + // The write side of the same rule: a record with no binding is serialized by SerializeUntyped, and a payload nested past + // the limit is refused there, before it can reach the wire. + [Test] + public void ToRawRecordWithNoBindingRefusesAPayloadNestedPastTheLimit() + { + var registry = new CultNetDocumentRegistry(Documents); + var descriptor = Documents.GetRequired(typeof(DeepDoc)); + DeepDoc Chain(int links) + { + var head = new DeepDoc(); + var tip = head; + for (var i = 0; i < links; i++) tip = tip.Inner = new DeepDoc(); + return head; + } + + // Each link is one array; the root is depth 1, so 255 links is depth 256 (at the limit) and 256 links is 257. + Assert.That(registry.ToRawRecord(descriptor, new CultRecordKey("a"), Chain(255), "1970-01-01T00:00:00.000Z").Payload, Is.Not.Empty); + var refused = Assert.Throws( + () => registry.ToRawRecord(descriptor, new CultRecordKey("a"), Chain(256), "1970-01-01T00:00:00.000Z"))!; + Assert.That(refused.Message, Does.Contain("refused on write")); + Assert.That(refused.Message, Does.Contain(typeof(DeepDoc).FullName)); + } } }