优化配置映射逻辑并新增单元测试覆盖
石头 authored at 2025-09-22 23:28:22
6.60 KiB
X
using NewLife.Configuration;
using Xunit;

namespace XUnitTest.Configuration;

public class ConfigHelperPrivateTests
{
    private static IConfigSection NewRoot() => new ConfigSection { Key = "Root", Childs = [] };
    private static IConfigSection NewElement(String key, String? value) => new ConfigSection { Key = key, Value = value };

    // 覆盖 MapToObject/MapToArray/MapToList:包含基元、数组、列表、复杂类型
    [Fact]
    public void MapTo_Mixes_Base_Array_List_Complex()
    {
        // Arrange
        var root = NewRoot();
        root.AddChild("BoolProp").Value = "true";

        var intArray = root.AddChild("IntArray");
        intArray.Childs = [
            NewElement("x", "1"),
            NewElement("y", "2")
        ];

        var complexProp = root.AddChild("ComplexProp");
        complexProp.Childs = [
            NewElement("Name", "node"),
            NewElement("Value", "7"),
        ];

        var complexList = root.AddChild("ComplexList");
        complexList.Childs = [
            new ConfigSection
            {
                Key = "item1",
                Childs = [NewElement("Name", "n1"), NewElement("Value", "1")]
            },
            new ConfigSection
            {
                Key = "item2",
                Childs = [NewElement("Name", "n2"), NewElement("Value", "2")]
            },
        ];

        var complexArray = root.AddChild("ComplexArray");
        complexArray.Childs = [
            new ConfigSection
            {
                Key = "node1",
                Childs = [NewElement("Name", "a"), NewElement("Value", "10")]
            },
            new ConfigSection
            {
                Key = "node2",
                Childs = [NewElement("Name", "b"), NewElement("Value", "20")]
            },
        ];

        var model = new ComplexModel
        {
            // 预填充以验证数组长度会被重建、列表会被清空
            IntArray = [9, 9, 9],
            ComplexList = [new Node { Name = "x", Value = 100 }]
        };
        var provider = new XmlConfigProvider();

        // Act
        root.MapTo(model, provider);

        // Assert 基元
        Assert.True(model.BoolProp);

        // Assert 数组(基元)
        Assert.Equal(new[] { 1, 2 }, model.IntArray);

        // Assert 复杂属性(空时自动实例化并递归映射)
        Assert.NotNull(model.ComplexProp);
        Assert.Equal("node", model.ComplexProp.Name);
        Assert.Equal(7, model.ComplexProp.Value);

        // Assert 列表(复杂元素),并验证原内容被清空
        Assert.NotNull(model.ComplexList);
        Assert.Equal(2, model.ComplexList.Count);
        Assert.Equal(["n1", "n2"], model.ComplexList.Select(e => e.Name).ToArray());
        Assert.Equal([1, 2], model.ComplexList.Select(e => e.Value).ToArray());

        // Assert 数组(复杂元素)
        Assert.NotNull(model.ComplexArray);
        Assert.Equal(2, model.ComplexArray.Length);
        Assert.Equal(["a", "b"], model.ComplexArray.Select(e => e.Name).ToArray());
        Assert.Equal([10, 20], model.ComplexArray.Select(e => e.Value).ToArray());
    }

    // 覆盖 MapToArray:当目标数组长度与配置不一致时,按配置重建
    [Fact]
    public void MapToArray_RebuildsLength()
    {
        var root = NewRoot();
        var arrSec = root.AddChild("IntArray");
        arrSec.Childs = [NewElement("x", "10"), NewElement("y", "20")];

        var model = new ComplexModel { IntArray = [1, 2, 3] };
        var provider = new XmlConfigProvider();

        root.MapTo(model, provider);

        Assert.Equal(new[] { 10, 20 }, model.IntArray);
    }

    // 覆盖 MapToList:当已有列表实例时会清空并填充
    [Fact]
    public void MapToList_ClearsExisting()
    {
        var root = NewRoot();
        var numbers = root.AddChild("Numbers");
        numbers.Childs = [NewElement(nameof(Int32), "1"), NewElement(nameof(Int32), "2")];

        var model = new ListModel { Numbers = [100, 200] };
        var provider = new XmlConfigProvider();

        root.MapTo(model, provider);

        Assert.Equal(new[] { 1, 2 }, model.Numbers);
    }

    // 覆盖 MapObject 和 MapArray:从模型到配置,包含基元、列表(基元与复杂)、复杂对象
    [Fact]
    public void MapFrom_Covers_Base_List_Complex_And_Enum()
    {
        var root = NewRoot();
        var model = new ComplexModel
        {
            BoolProp = true,
            EnumProp = DayOfWeek.Friday,
            IntArray = [5, 6], // 注意:MapFrom 针对数组不一定进入 MapArray,这里主要覆盖列表路径
            Numbers = [3, 4],
            ComplexProp = new Node { Name = "p", Value = 9 },
            ComplexList = [new Node { Name = "l1", Value = 1 }, new Node { Name = "l2", Value = 2 }],
        };

        root.MapFrom(model);

        // 基元与枚举(SetValue 走到布尔与枚举分支)
        Assert.Equal("true", root.Find("BoolProp")?.Value);
        Assert.Equal("Friday", root.Find("EnumProp")?.Value);

        // 列表(基元)
        var numbers = root.Find("Numbers");
        Assert.NotNull(numbers);
        Assert.NotNull(numbers!.Childs);
        Assert.Equal(2, numbers.Childs!.Count);
        Assert.All(numbers.Childs!, c => Assert.Equal(nameof(Int32), c.Key));
        Assert.Equal(["3", "4"], numbers.Childs!.Select(e => e.Value).ToArray());

        // 复杂对象
        var cp = root.Find("ComplexProp");
        Assert.NotNull(cp);
        Assert.Equal("p", cp!.Childs!.First(e => e.Key == "Name").Value);
        Assert.Equal("9", cp!.Childs!.First(e => e.Key == "Value").Value);

        // 列表(复杂元素)
        var cl = root.Find("ComplexList");
        Assert.NotNull(cl);
        Assert.NotNull(cl!.Childs);
        Assert.Equal(2, cl.Childs!.Count);
        // 验证 MapArray 使用元素类型名作为子节点 Key(Node)
        Assert.All(cl.Childs!, c => Assert.Equal(nameof(Node), c.Key));
        Assert.Equal(["l1", "l2"], cl.Childs!.Select(e => e.Childs!.First(x => x.Key == "Name").Value).ToArray());
    }

    private sealed class ListModel
    {
        public List<Int32> Numbers { get; set; }
    }

    private sealed class ComplexModel
    {
        public Boolean BoolProp { get; set; }
        public DayOfWeek EnumProp { get; set; }

        public Int32[] IntArray { get; set; }
        public List<Int32> Numbers { get; set; }

        public Node ComplexProp { get; set; }
        public List<Node> ComplexList { get; set; }
        public Node[] ComplexArray { get; set; }
    }

    private sealed class Node
    {
        public String Name { get; set; }
        public Int32 Value { get; set; }
    }
}