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

XFEExtension

【DLL】XFE各类拓展是一个C#的DLL库,旨在优化C#代码中常用语句的使用,并提供更简洁的访问方式,同时提供Xunit测试框架,快速搭建服务器/客户端,免费ChatGPTAPI接口,免费通讯服务器,XFE下载器,新增格式等

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Collections.Concurrent;
using XFEExtension.NetCore.XFETransform.Json;

namespace XFEExtension.NetCore.Test.Json;

public class XFEJsonNavigationTests
{
    [Fact]
    public void Parse_IsLazy_AndEarlyPropertyDoesNotReadInvalidTail()
    {
        var node = XFEJson.Parse("{\"first\":1,\"invalid\":[}");

        Assert.Equal(0, node.ScannedCharacters);
        Assert.Equal(1, node["first"]!.GetInt32());
        var afterFirstRead = node.ScannedCharacters;
        Assert.True(afterFirstRead < 20);
        Assert.Equal(1, node["first"]!.GetInt32());
        Assert.Equal(afterFirstRead, node.ScannedCharacters);
        Assert.Throws<XFEJsonException>(node.Validate);
    }

    [Fact]
    public void Navigation_SupportsObjectsArraysOperatorsAndProjection()
    {
        XFEJsonNode node = """
            {
              "data": {
                "items": [
                  { "id": 1, "name": "one", "ignored": { "deep": true } },
                  { "id": 2, "name": "two" }
                ]
              }
            }
            """;

        Assert.Equal(2, (node > "data" > "items" > 1 > "id")!.GetInt32());
        Assert.Equal("one", node["data"]!["items"]![0]!["name"]!.GetString());
        Assert.Null(node["missing"]);
        Assert.Null(node["data"]!["items"]![99]);

        var projections = node["data"]!["items"]!.ProjectArray("id", "name", "missing").ToArray();
        Assert.Equal(2, projections.Length);
        Assert.Equal(1, projections[0]["id"]!.GetInt32());
        Assert.Null(projections[0]["missing"]);
    }

    [Fact]
    public void StringsAndNumbers_FollowStrictJsonRules()
    {
        var node = XFEJson.Parse("""
            {
              "text": "quote:\" slash:\\ solidus:\/ controls:\b\f\n\r\t",
              "unicode": "\u4F60\u597D \uD83D\uDE00",
              "number": -1.25e+3,
              "null": null
            }
            """);

        Assert.Equal("quote:\" slash:\\ solidus:/ controls:\b\f\n\r\t", node["text"]!.GetString());
        Assert.Equal("你好 😀", node["unicode"]!.GetString());
        Assert.Equal(-1250d, node["number"]!.GetDouble());
        Assert.True(node["null"]!.IsNull);
        node.Validate();
    }

    [Theory]
    [InlineData("")]
    [InlineData("01")]
    [InlineData("1.")]
    [InlineData("1e")]
    [InlineData("TRUE")]
    [InlineData("{'a':1}")]
    [InlineData("{\"a\":1,}")]
    [InlineData("[1,]")]
    [InlineData("\"\\x\"")]
    [InlineData("true false")]
    public void Validate_RejectsInvalidJson(string json)
    {
        Assert.False(XFEJson.TryValidate(json, out var error));
        Assert.NotNull(error);
        Assert.True(error.Position >= 0);
        Assert.StartsWith("$", error.Path);
    }

    [Fact]
    public void LargeArray_DoesNotOverflowShortIndex()
    {
        const int count = 40_000;
        var text = "[" + string.Join(',', Enumerable.Range(0, count)) + "]";
        var node = XFEJson.Parse(text);

        Assert.Equal(count - 1, node[count - 1]!.GetInt32());
        Assert.Equal(count, node.Count);
        node.Validate();
    }

    [Fact]
    public void ConcurrentReads_AreSafeAndConsistent()
    {
        var text = "{" + string.Join(',', Enumerable.Range(0, 500).Select(index => $"\"p{index}\":{index}")) + "}";
        var node = XFEJson.Parse(text);
        var failures = new ConcurrentQueue<int>();

        Parallel.For(0, 2_000, index =>
        {
            var expected = index % 500;
            if (node[$"p{expected}"]?.GetInt32() != expected)
                failures.Enqueue(expected);
        });

        Assert.Empty(failures);
        Assert.Equal(500, node.Count);
    }

    [Fact]
    public void FullValidation_ScansLinearlyWithInputLength()
    {
        var smallText = "[" + string.Join(',', Enumerable.Range(0, 1_000)) + "]";
        var largeText = "[" + string.Join(',', Enumerable.Range(0, 10_000)) + "]";
        var small = XFEJson.Parse(smallText);
        var large = XFEJson.Parse(largeText);

        small.Validate();
        large.Validate();

        var inputRatio = (double)largeText.Length / smallText.Length;
        var scanRatio = (double)large.ScannedCharacters / small.ScannedCharacters;
        Assert.InRange(scanRatio / inputRatio, 0.95, 1.05);
    }
}