using Bunit; using Microsoft.AspNetCore.Components; using ScadaLink.CentralUI.Components.Shared; namespace ScadaLink.CentralUI.Tests.Shared; /// /// Regression tests for CentralUI-018. TreeView's storage-restore path /// called JsonSerializer.Deserialize on the raw treeviewStorage /// payload outside any try block — a corrupt payload threw an uncaught /// JsonException during OnAfterRenderAsync, breaking the /// component. The fix guards the deserialize and ignores a corrupt payload. /// public class TreeViewStorageResilienceTests : BunitContext { private record TestNode(string Key, string Label, List Children); private static List Roots() => new() { new("a", "Alpha", new() { new("a1", "Alpha-1", new()) }), new("b", "Beta", new()), }; private IRenderedComponent> BuildTree() => Render>(parameters => parameters .Add(p => p.Items, Roots()) .Add(p => p.ChildrenSelector, n => n.Children) .Add(p => p.HasChildrenSelector, n => n.Children.Count > 0) .Add(p => p.KeySelector, n => n.Key) .Add(p => p.NodeContent, (RenderFragment)(node => b => b.AddMarkupContent(0, $"{node.Label}"))) .Add(p => p.StorageKey, "corrupt-tree")); [Fact] public void StorageRestore_CorruptJsonPayload_DoesNotThrow_AndStillRenders() { // A garbage payload that is not valid JSON for a List. JSInterop.Setup("treeviewStorage.load", _ => true) .SetResult("{not json at all]"); JSInterop.SetupVoid("treeviewStorage.save", _ => true); // Pre-fix: OnAfterRenderAsync threw JsonException out of the unguarded // Deserialize call. Post-fix: the corrupt payload is ignored. var cut = BuildTree(); Assert.Contains("Alpha", cut.Markup); Assert.Contains("Beta", cut.Markup); } [Fact] public void StorageRestore_WrongShapeJson_DoesNotThrow() { // Valid JSON, but not a List — an object, not an array. JSInterop.Setup("treeviewStorage.load", _ => true) .SetResult("{\"unexpected\": true}"); JSInterop.SetupVoid("treeviewStorage.save", _ => true); var cut = BuildTree(); Assert.Contains("Alpha", cut.Markup); } }