XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Security.Cryptography;
using System.Text.Json;
using Xunit;

namespace XFESystem.Documents.Tests;

public sealed class WebsiteWebPreviewTests
{
    [Fact]
    public void NativeWebsiteSnapshotKeepsEveryImportedSourceByteAndLicenseReference()
    {
        using var manifest = JsonDocument.Parse(WebsiteAssets.ReadText("Exact/SourceManifest.json"));
        Assert.Equal(2, manifest.RootElement.GetProperty("SchemaVersion").GetInt32());
        Assert.Equal("XFEStudioWebSite", manifest.RootElement.GetProperty("SourceRepository").GetString());
        foreach (var entry in manifest.RootElement.GetProperty("Entries").EnumerateArray())
        {
            var expected = entry.GetProperty("SourceSha256").GetString();
            Assert.Equal(expected, entry.GetProperty("SnapshotSha256").GetString());
            using var asset = WebsiteAssets.Open("Exact/" + entry.GetProperty("SnapshotPath").GetString());
            Assert.Equal(expected, Convert.ToHexStringLower(SHA256.HashData(asset)));
            Assert.False(string.IsNullOrWhiteSpace(entry.GetProperty("License").GetString()));
        }
    }

    [Fact]
    public void NativeWebsitePreviewKeepsOriginalPartialsAndAddsAnExplicitFlatDesktopHostStylesheet()
    {
        var preview = new DocumentPreviewBuilder(new()).MarkdownWeb("# XFE\n\n```csharp\npublic class Sample {}\n```", "Sample.md");
        Assert.Contains("class=\"markdown-body docs-article-body\"", preview.Html);
        Assert.Contains("data-bs-theme=\"dark\"", preview.Html);
        Assert.DoesNotContain("<style", preview.Html);
        Assert.DoesNotContain("data-manual-doc-init", preview.Html);
        Assert.DoesNotContain("data-tool-label", preview.Html);
        Assert.DoesNotContain("Content-Security-Policy", preview.Html);
        Assert.Contains("script-src 'self'", preview.ContentSecurityPolicy);
        Assert.True(preview.Html.IndexOf("/css/bootstrap.min.css", StringComparison.Ordinal) < preview.Html.IndexOf("/css/theme-variables.css", StringComparison.Ordinal));
        Assert.True(preview.Html.IndexOf("/js/docs/doc-utils.js", StringComparison.Ordinal) < preview.Html.IndexOf("/js/docs/code-block-tools.js", StringComparison.Ordinal));
        Assert.True(preview.Html.IndexOf("/css/docs/docs-layout.css", StringComparison.Ordinal) < preview.Html.IndexOf("/css/docs/xfesystem-desktop.css", StringComparison.Ordinal));
        Assert.Contains("class=\"docs-reading-progress\"", preview.Html);
        Assert.Contains("<span data-doc-progress>", preview.Html);
        Assert.Contains("language-csharp", preview.Html);
        Assert.True(preview.RequiresJavaScript);
    }

    [Theory]
    [InlineData(true, "dark", "class=\"dark\"")]
    [InlineData(false, "light", "class=\"\"")]
    public void WebsiteThemeSelectsTheOriginalThemeVariableRules(bool dark, string theme, string cssClass)
    {
        var preview = new DocumentPreviewBuilder(new()).MarkdownWeb("Hello", dark: dark);
        Assert.Contains(cssClass, preview.Html);
        Assert.Contains("data-bs-theme=\"" + theme + "\"", preview.Html);
        Assert.Contains("--main-color: #ac99ea", WebsiteAssets.ReadText("Exact/ThemeVariables.css"));
        Assert.Contains("--main-color: #512db4", WebsiteAssets.ReadText("Exact/ThemeVariables.css"));
    }

    [Fact]
    public void MarkdownContentCannotInjectExecutableScriptIntoTheTrustedDocumentWrapper()
    {
        var preview = new DocumentPreviewBuilder(new()).MarkdownWeb(
            "# Safe\n<script>globalThis.Evil=true;</script><a href=\"javascript:alert(1)\">Bad</a>" +
            "<img src=\"x\" onerror=\"globalThis.Evil=true\">", "</title><script>evil()</script>");
        Assert.DoesNotContain("globalThis.Evil", preview.Html);
        Assert.DoesNotContain("javascript:", preview.Html);
        Assert.DoesNotContain("<script>evil()", preview.Html);
        Assert.Contains("&lt;/title&gt;", preview.Html);
    }

    [Fact]
    public void CanonicalWebsiteUrlsMapToEmbeddedBytesWithoutRewritingTheirContents()
    {
        Assert.True(WebsiteAssets.TryOpenWebPath("/js/docs/doc-utils.js", out var script, out var scriptType));
        using (script)
        using (var reader = new StreamReader(script!))
        {
            Assert.Equal("application/javascript", scriptType);
            var content = reader.ReadToEnd();
            Assert.Contains("/js/docs/mermaid.min.js?v=11.16.0", content);
            Assert.DoesNotContain("xfe-resources.invalid", content);
        }
        Assert.True(WebsiteAssets.TryOpenWebPath("/css/docs/katex.min.css", out var style, out var styleType));
        using (style)
        using (var reader = new StreamReader(style!))
        {
            Assert.Equal("text/css", styleType);
            Assert.Contains("fonts/KaTeX_", reader.ReadToEnd());
        }
        foreach (var resource in WebsiteAssets.WebResources)
        {
            Assert.True(WebsiteAssets.TryOpenWebPath(resource.UrlPath, out var stream, out var type));
            using (stream) Assert.True(stream!.Length > 0);
            Assert.Equal(resource.ContentType, type);
        }
    }

    [Theory]
    [InlineData("/css/docs/../Secret")]
    [InlineData("/css/docs\\Secret")]
    [InlineData("/js/docs/doc-utils.js?outside=true")]
    [InlineData("https://outside.invalid/js/docs/doc-utils.js")]
    [InlineData("/js/docs//doc-utils.js")]
    public void ResourceRoutingDoesNotAcceptFilesystemPathsOrArbitraryUrls(string path)
    {
        Assert.False(WebsiteAssets.TryOpenWebPath(path, out var stream, out var type));
        Assert.Null(stream);
        Assert.Empty(type);
    }

    [Fact]
    public void CodePreviewUsesTheWebsitesCSharpGrammarAndEscapesMarkup()
    {
        var preview = new DocumentPreviewBuilder(new()).CodeWeb("public string Text = \"</script>\";", "Sample.cs");
        Assert.Contains("language-csharp", preview.Html);
        Assert.Contains("&lt;/script&gt;", preview.Html);
        Assert.Contains("/js/docs/csharp.js", preview.Html);
        Assert.DoesNotContain("monarch", preview.Html, StringComparison.OrdinalIgnoreCase);
    }
}