using System.Text; using Xunit; using Xunit.Abstractions; namespace XFESystem.Documents.Tests; public sealed class WebsiteAssetCacheTests(ITestOutputHelper output) { [Fact] public void EntireTextCatalogPreservesOriginalDecodedTextAndObjectIdentity() { const string prefix = "Documents/Website/"; var names = typeof(WebsiteAssets).Assembly.GetManifestResourceNames() .Where(name => name.StartsWith(prefix, StringComparison.Ordinal)) .Where(name => Path.GetExtension(name).ToLowerInvariant() is ".js" or ".css" or ".json" or ".md" or ".txt" or ".html" or ".htm") .Select(name => name[prefix.Length..]).ToArray(); Assert.Equal(names.Length, WebsiteAssets.PrewarmTextResources()); long characters = 0; foreach (string name in names) { using var original = new StreamReader(WebsiteAssets.Open(name)); string cached = WebsiteAssets.ReadText(name); Assert.Equal(original.ReadToEnd(), cached); Assert.Same(cached, WebsiteAssets.ReadText(name)); characters += cached.Length; } Assert.InRange(names.Length, 1, 512); Assert.InRange(characters, 1, 16_777_216); output.WriteLine("Website text cache: {0} entries, {1} UTF-16 characters; embedded bytes remain unchanged.", names.Length, characters); } [Fact] public void MermaidHitsReuseOneStringAcrossThreadsAndAvoidLargeTransientAllocations() { string expected = WebsiteAssets.ReadText("Exact/Mermaid.min.js"); Assert.True(expected.Length > 3_000_000); Parallel.For(0, 32, _ => Assert.Same(expected, WebsiteAssets.ReadText("Exact/Mermaid.min.js"))); long before = GC.GetAllocatedBytesForCurrentThread(); for (int read = 0; read < 64; read++) Assert.Same(expected, WebsiteAssets.ReadText("Exact/Mermaid.min.js")); Assert.InRange(GC.GetAllocatedBytesForCurrentThread() - before, 0, 1_048_576); Assert.True(WebsiteAssets.TryReadWebText("/js/docs/mermaid.min.js", out string routed, out string mime)); Assert.Same(expected, routed); Assert.Equal("application/javascript", mime); } [Fact] public void WebTextRoutesKeepOriginalBinaryFallbackAndRejectInvalidPaths() { foreach (var resource in WebsiteAssets.WebResources) { Assert.True(WebsiteAssets.TryReadWebText(resource.UrlPath, out string text, out string mime)); using var original = new StreamReader(WebsiteAssets.Open(resource.AssetName), Encoding.UTF8, true); Assert.Equal(original.ReadToEnd(), text); Assert.Equal(resource.ContentType, mime); } Assert.False(WebsiteAssets.TryReadWebText("/js/docs/../mermaid.min.js", out _, out _)); Assert.False(WebsiteAssets.TryReadWebText("/js/docs/mermaid.min.js?changed=1", out _, out _)); Assert.Throws(() => WebsiteAssets.ReadText("../Mermaid.min.js")); Assert.Throws(() => WebsiteAssets.ReadText("Missing.min.js")); } }