Skip to content

Commit 0879819

Browse files
test(audience): pin Json.Serialize depth guard and cycle detection
Both guards exist because their absence yields StackOverflowException, which the .NET runtime does not catch — the process terminates. Neither guard had a test. Three new tests: - nesting one level past MaxDepth throws FormatException with "nesting exceeds" message. Sabotage: disabling GuardDepth fails the test. - self-referential dict throws FormatException with "cycle" message. Sabotage: disabling the visited.Add(container) check makes the test recurse into the depth guard and the assertion on "cycle" text fails. - shared-child diamond (same dict in two sibling keys) is not treated as a cycle. Sabotage: removing the post-recursion visited.Remove makes the second sibling throw "cycle". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 47f70c2 commit 0879819

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

src/Packages/Audience/Tests/Runtime/Utility/JsonTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using NUnit.Framework;
34

@@ -203,5 +204,48 @@ public void Serialize_RealisticEventPayload_ProducesCorrectJson()
203204
StringAssert.Contains("\"perfect\":true", result);
204205
StringAssert.Contains("\"tags\":[\"fast\",\"clean\"]", result);
205206
}
207+
208+
[Test]
209+
public void Serialize_NestingExceedsMaxDepth_ThrowsFormatException()
210+
{
211+
var root = new Dictionary<string, object>();
212+
var current = root;
213+
for (var i = 0; i < Json.MaxDepth; i++)
214+
{
215+
var next = new Dictionary<string, object>();
216+
current["next"] = next;
217+
current = next;
218+
}
219+
220+
var ex = Assert.Throws<FormatException>(() => Json.Serialize(root));
221+
StringAssert.Contains("nesting exceeds", ex.Message);
222+
}
223+
224+
[Test]
225+
public void Serialize_SelfReferentialDict_ThrowsFormatException()
226+
{
227+
var root = new Dictionary<string, object>();
228+
root["self"] = root;
229+
230+
var ex = Assert.Throws<FormatException>(() => Json.Serialize(root));
231+
StringAssert.Contains("cycle", ex.Message);
232+
}
233+
234+
[Test]
235+
public void Serialize_SharedChildInSiblingKeys_IsNotTreatedAsCycle()
236+
{
237+
// Diamond: visited set tracks the current recursion stack, not all objects ever seen.
238+
var shared = new Dictionary<string, object> { ["k"] = "v" };
239+
var root = new Dictionary<string, object>
240+
{
241+
["a"] = shared,
242+
["b"] = shared,
243+
};
244+
245+
var result = Json.Serialize(root);
246+
247+
StringAssert.Contains("\"a\":{\"k\":\"v\"}", result);
248+
StringAssert.Contains("\"b\":{\"k\":\"v\"}", result);
249+
}
206250
}
207251
}

0 commit comments

Comments
 (0)