优化分页参数类并新增全面单元测试
石头 authored at 2025-09-26 01:01:49
19.55 KiB
X
using NewLife.Data;
using Xunit;

namespace XUnitTest.Core.Data;

/// <summary>分页参数测试</summary>
public class PageParameterTests
{
    #region 构造函数测试
    [Fact(DisplayName = "默认构造函数应设置正确的默认值")]
    public void Constructor_Default_ShouldSetCorrectDefaults()
    {
        // Arrange & Act
        var page = new PageParameter();

        // Assert
        Assert.Equal(1, page.PageIndex);
        Assert.Equal(20, page.PageSize);
        Assert.Null(page.Sort);
        Assert.False(page.Desc);
        Assert.Null(page.OrderBy);
        Assert.Equal(-1, page.StartRow);
        Assert.Equal(0, page.TotalCount);
        Assert.False(page.RetrieveTotalCount);
        Assert.Null(page.State);
        Assert.False(page.RetrieveState);
    }

    [Fact(DisplayName = "复制构造函数应正确复制所有属性")]
    public void Constructor_Copy_ShouldCopyAllProperties()
    {
        // Arrange - 注意:由于Sort会清空OrderBy,所以这里只测试Sort的情况
        var original = new PageParameter
        {
            PageIndex = 2,
            PageSize = 50,
            Sort = "Name",
            Desc = true,
            StartRow = 100,
            TotalCount = 1000,
            RetrieveTotalCount = true,
            State = "test state",
            RetrieveState = true
        };

        // Act
        var copy = new PageParameter(original);

        // Assert
        Assert.Equal(original.PageIndex, copy.PageIndex);
        Assert.Equal(original.PageSize, copy.PageSize);
        Assert.Equal(original.Sort, copy.Sort);
        Assert.Equal(original.Desc, copy.Desc);
        Assert.Null(copy.OrderBy); // Sort会清空OrderBy
        Assert.Equal(original.StartRow, copy.StartRow);
        Assert.Equal(original.TotalCount, copy.TotalCount);
        Assert.Equal(original.RetrieveTotalCount, copy.RetrieveTotalCount);
        Assert.Equal(original.State, copy.State);
        Assert.Equal(original.RetrieveState, copy.RetrieveState);
    }

    [Fact(DisplayName = "复制构造函数会清空OrderBy(由于CopyFrom实现)")]
    public void Constructor_Copy_WillClearOrderByDueToImplementation()
    {
        // Arrange - 只设置OrderBy,不设置Sort
        var original = new PageParameter
        {
            PageIndex = 2,
            PageSize = 50,
            StartRow = 100,
            TotalCount = 1000,
            RetrieveTotalCount = true,
            State = "test state",
            RetrieveState = true
        };
        // 直接设置OrderBy而不通过Sort
        original.OrderBy = "Custom ORDER BY";

        // Act
        var copy = new PageParameter(original);

        // Assert - 由于CopyFrom的实现,OrderBy会被清空
        Assert.Null(copy.OrderBy); // CopyFrom中Sort=pm.Sort会清空OrderBy
        Assert.Null(copy.Sort); // 源对象的Sort确实是null
    }
    #endregion

    #region Sort属性测试
    [Fact(DisplayName = "设置Sort应清空OrderBy")]
    public void Sort_Set_ShouldClearOrderBy()
    {
        // Arrange
        var page = new PageParameter { OrderBy = "Custom ORDER BY" };

        // Act
        page.Sort = "Name";

        // Assert
        Assert.Equal("Name", page.Sort);
        Assert.Null(page.OrderBy);
    }

    [Theory(DisplayName = "Sort应正确解析Asc方向")]
    [InlineData("Name asc", "Name", false)]
    [InlineData("Name ASC", "Name", false)]
    [InlineData("Name Asc", "Name", false)]
    [InlineData("  Name   asc  ", "Name", false)]
    public void Sort_SetWithAsc_ShouldParseCorrectly(String input, String expectedSort, Boolean expectedDesc)
    {
        // Arrange
        var page = new PageParameter();

        // Act
        page.Sort = input;

        // Assert
        Assert.Equal(expectedSort, page.Sort);
        Assert.Equal(expectedDesc, page.Desc);
    }

    [Theory(DisplayName = "Sort应正确解析Desc方向")]
    [InlineData("Name desc", "Name", true)]
    [InlineData("Name DESC", "Name", true)]
    [InlineData("Name Desc", "Name", true)]
    [InlineData("  Name   desc  ", "Name", true)]
    public void Sort_SetWithDesc_ShouldParseCorrectly(String input, String expectedSort, Boolean expectedDesc)
    {
        // Arrange
        var page = new PageParameter();

        // Act
        page.Sort = input;

        // Assert
        Assert.Equal(expectedSort, page.Sort);
        Assert.Equal(expectedDesc, page.Desc);
    }

    [Theory(DisplayName = "Sort不包含方向时应保持原字段")]
    [InlineData("Name", "Name")]
    [InlineData("CreateTime", "CreateTime")]
    [InlineData("User.Name", "User.Name")]
    public void Sort_SetWithoutDirection_ShouldKeepOriginal(String input, String expected)
    {
        // Arrange
        var page = new PageParameter();

        // Act
        page.Sort = input;

        // Assert
        Assert.Equal(expected, page.Sort);
        Assert.False(page.Desc); // 默认应为false
    }

    [Theory(DisplayName = "Sort包含逗号时不应解析方向")]
    [InlineData("Name,Age desc", "Name,Age desc")]
    [InlineData("Name asc, Age desc", "Name asc, Age desc")]
    public void Sort_SetWithComma_ShouldNotParseDirection(String input, String expected)
    {
        // Arrange
        var page = new PageParameter();

        // Act
        page.Sort = input;

        // Assert
        Assert.Equal(expected, page.Sort);
        Assert.False(page.Desc); // 不应被解析
    }

    [Fact(DisplayName = "Sort设置为null或空应正确处理")]
    public void Sort_SetNullOrEmpty_ShouldHandleCorrectly()
    {
        // Arrange
        var page = new PageParameter();

        // Act & Assert - null
        page.Sort = null;
        Assert.Null(page.Sort);

        // Act & Assert - empty
        page.Sort = "";
        Assert.Equal("", page.Sort);

        // Act & Assert - whitespace
        page.Sort = "   ";
        Assert.Equal("", page.Sort); // 应被Trim为空
    }
    #endregion

    #region PageCount属性测试
    [Theory(DisplayName = "PageCount应正确计算页数")]
    [InlineData(0, 20, 0)] // 总数为0时,页数为0(实际行为)
    [InlineData(1, 20, 1)]
    [InlineData(20, 20, 1)]
    [InlineData(21, 20, 2)]
    [InlineData(40, 20, 2)]
    [InlineData(41, 20, 3)]
    [InlineData(100, 30, 4)]
    [InlineData(101, 30, 4)]
    public void PageCount_Calculate_ShouldReturnCorrectValue(Int64 totalCount, Int32 pageSize, Int64 expectedPageCount)
    {
        // Arrange
        var page = new PageParameter
        {
            TotalCount = totalCount,
            PageSize = pageSize
        };

        // Act & Assert
        Assert.Equal(expectedPageCount, page.PageCount);
    }

    [Fact(DisplayName = "PageSize为0时PageCount应返回1")]
    public void PageCount_WhenPageSizeIsZero_ShouldReturnOne()
    {
        // Arrange
        var page = new PageParameter
        {
            TotalCount = 100,
            PageSize = 0
        };

        // Act & Assert
        Assert.Equal(1, page.PageCount);
    }

    [Fact(DisplayName = "PageSize为负数时PageCount应返回1")]
    public void PageCount_WhenPageSizeIsNegative_ShouldReturnOne()
    {
        // Arrange
        var page = new PageParameter
        {
            TotalCount = 100,
            PageSize = -10
        };

        // Act & Assert
        Assert.Equal(1, page.PageCount);
    }
    #endregion

    #region CopyFrom方法测试
    [Fact(DisplayName = "CopyFrom应正确复制Sort相关属性")]
    public void CopyFrom_WithSort_ShouldCopyCorrectly()
    {
        // Arrange
        var source = new PageParameter
        {
            Sort = "Name",
            Desc = true,
            PageIndex = 3,
            PageSize = 50,
            StartRow = 200,
            TotalCount = 2000,
            RetrieveTotalCount = true,
            State = "test state",
            RetrieveState = true
        };
        var target = new PageParameter();

        // Act
        var result = target.CopyFrom(source);

        // Assert
        Assert.Same(target, result); // 应返回当前实例
        Assert.Equal(source.Sort, target.Sort);
        Assert.Equal(source.Desc, target.Desc);
        Assert.Null(target.OrderBy); // Sort会清空OrderBy
        Assert.Equal(source.PageIndex, target.PageIndex);
        Assert.Equal(source.PageSize, target.PageSize);
        Assert.Equal(source.StartRow, target.StartRow);
        Assert.Equal(source.TotalCount, target.TotalCount);
        Assert.Equal(source.RetrieveTotalCount, target.RetrieveTotalCount);
        Assert.Equal(source.State, target.State);
        Assert.Equal(source.RetrieveState, target.RetrieveState);
    }

    [Fact(DisplayName = "CopyFrom会清空OrderBy(由于实现原因)")]
    public void CopyFrom_WillClearOrderByDueToImplementation()
    {
        // Arrange
        var source = new PageParameter
        {
            PageIndex = 3,
            PageSize = 50,
            StartRow = 200,
            TotalCount = 2000,
            RetrieveTotalCount = true,
            State = "test state",
            RetrieveState = true
        };
        // 直接设置OrderBy而不设置Sort
        source.OrderBy = "Custom ORDER BY";
        
        var target = new PageParameter();

        // Act
        var result = target.CopyFrom(source);

        // Assert - 由于CopyFrom实现中Sort=pm.Sort会清空OrderBy
        Assert.Same(target, result); // 应返回当前实例
        Assert.Null(target.OrderBy); // 被清空了
        Assert.Null(target.Sort); // 源对象的Sort确实是null
        Assert.Equal(source.PageIndex, target.PageIndex);
        Assert.Equal(source.PageSize, target.PageSize);
        Assert.Equal(source.StartRow, target.StartRow);
        Assert.Equal(source.TotalCount, target.TotalCount);
        Assert.Equal(source.RetrieveTotalCount, target.RetrieveTotalCount);
        Assert.Equal(source.State, target.State);
        Assert.Equal(source.RetrieveState, target.RetrieveState);
    }

    [Fact(DisplayName = "CopyFrom应体现Sort优先级高于OrderBy的特性")]
    public void CopyFrom_WithBothSortAndOrderBy_ShouldPrioritizeSort()
    {
        // Arrange - 创建一个同时有Sort和OrderBy的源对象(通过特殊方式)
        var source = new PageParameter();
        source.OrderBy = "Custom ORDER BY"; // 先设置OrderBy
        source.Sort = "Name"; // 这会清空OrderBy
        source.OrderBy = "Custom ORDER BY"; // 重新设置OrderBy(这是实际可能的情况)
        
        var target = new PageParameter();

        // Act
        var result = target.CopyFrom(source);

        // Assert - 根据CopyFrom的实现,Sort会清空OrderBy
        Assert.Same(target, result);
        Assert.Equal("Name", target.Sort);
        Assert.Null(target.OrderBy); // Sort会清空OrderBy
    }

    [Fact(DisplayName = "CopyFrom传入null应返回当前实例不变")]
    public void CopyFrom_WithNull_ShouldReturnSelfUnchanged()
    {
        // Arrange
        var original = new PageParameter
        {
            PageIndex = 2,
            PageSize = 30,
            Sort = "Original"
        };

        // Act
        var result = original.CopyFrom(null);

        // Assert
        Assert.Same(original, result);
        Assert.Equal(2, original.PageIndex);
        Assert.Equal(30, original.PageSize);
        Assert.Equal("Original", original.Sort);
    }
    #endregion

    #region GetKey方法测试
    [Fact(DisplayName = "GetKey应返回正确的键值格式")]
    public void GetKey_ShouldReturnCorrectFormat()
    {
        // Arrange
        var page = new PageParameter
        {
            PageIndex = 2,
            TotalCount = 100,
            PageSize = 20,
            OrderBy = "Name DESC"
        };

        // Act
        var key = page.GetKey();

        // Assert
        var expectedKey = $"2-5-Name DESC"; // PageIndex-PageCount-OrderBy
        Assert.Equal(expectedKey, key);
    }

    [Fact(DisplayName = "GetKey在OrderBy为null时应正确处理")]
    public void GetKey_WithNullOrderBy_ShouldHandleCorrectly()
    {
        // Arrange
        var page = new PageParameter
        {
            PageIndex = 1,
            TotalCount = 50,
            PageSize = 10,
            OrderBy = null
        };

        // Act
        var key = page.GetKey();

        // Assert
        var expectedKey = "1-5-"; // PageIndex-PageCount-空OrderBy
        Assert.Equal(expectedKey, key);
    }
    #endregion

    #region IsValid方法测试
    [Theory(DisplayName = "IsValid应正确验证有效参数")]
    [InlineData(1, 20, true)]
    [InlineData(1, 0, true)] // PageSize为0表示不分页,应该有效
    [InlineData(10, 50, true)]
    public void IsValid_WithValidParameters_ShouldReturnTrue(Int32 pageIndex, Int32 pageSize, Boolean expected)
    {
        // Arrange
        var page = new PageParameter
        {
            PageIndex = pageIndex,
            PageSize = pageSize
        };

        // Act & Assert
        Assert.Equal(expected, page.IsValid());
    }

    [Theory(DisplayName = "IsValid应正确验证无效参数")]
    [InlineData(0, 20, false)] // PageIndex不能为0
    [InlineData(-1, 20, false)] // PageIndex不能为负数
    [InlineData(1, -1, false)] // PageSize不能为负数
    [InlineData(0, -1, false)] // 都无效
    public void IsValid_WithInvalidParameters_ShouldReturnFalse(Int32 pageIndex, Int32 pageSize, Boolean expected)
    {
        // Arrange
        var page = new PageParameter
        {
            PageIndex = pageIndex,
            PageSize = pageSize
        };

        // Act & Assert
        Assert.Equal(expected, page.IsValid());
    }
    #endregion

    #region Reset方法测试
    [Fact(DisplayName = "Reset应将所有属性重置为默认值")]
    public void Reset_ShouldResetAllPropertiesToDefaults()
    {
        // Arrange
        var page = new PageParameter
        {
            PageIndex = 5,
            PageSize = 100,
            Sort = "Name",
            OrderBy = "Custom ORDER BY",
            Desc = true,
            StartRow = 200,
            TotalCount = 1000,
            RetrieveTotalCount = true,
            State = "test state",
            RetrieveState = true
        };

        // Act
        page.Reset();

        // Assert
        Assert.Equal(1, page.PageIndex);
        Assert.Equal(20, page.PageSize);
        Assert.Null(page.Sort);
        Assert.Null(page.OrderBy);
        Assert.False(page.Desc);
        Assert.Equal(-1, page.StartRow);
        Assert.Equal(0, page.TotalCount);
        Assert.False(page.RetrieveTotalCount);
        Assert.Null(page.State);
        Assert.False(page.RetrieveState);
    }
    #endregion

    #region 边界和异常测试
    [Fact(DisplayName = "大数值应正确处理")]
    public void LargeValues_ShouldHandleCorrectly()
    {
        // Arrange & Act
        var page = new PageParameter
        {
            PageIndex = Int32.MaxValue,
            PageSize = Int32.MaxValue,
            TotalCount = Int64.MaxValue,
            StartRow = Int64.MaxValue
        };

        // Assert
        Assert.Equal(Int32.MaxValue, page.PageIndex);
        Assert.Equal(Int32.MaxValue, page.PageSize);
        Assert.Equal(Int64.MaxValue, page.TotalCount);
        Assert.Equal(Int64.MaxValue, page.StartRow);
        Assert.True(page.IsValid());
    }

    [Fact(DisplayName = "复杂排序字段解析应正确处理")]
    public void ComplexSortField_ShouldParseCorrectly()
    {
        // Arrange
        var page = new PageParameter();

        // Act & Assert - 包含点号的字段名
        page.Sort = "User.Name desc";
        Assert.Equal("User.Name", page.Sort);
        Assert.True(page.Desc);

        // Act & Assert - 包含下划线的字段名  
        page.Sort = "created_time asc";
        Assert.Equal("created_time", page.Sort);
        Assert.False(page.Desc);

        // Act & Assert - 纯数字字段名
        page.Sort = "123 desc";
        Assert.Equal("123", page.Sort);
        Assert.True(page.Desc);
    }

    [Fact(DisplayName = "State属性应支持任意对象类型")]
    public void State_ShouldSupportAnyObjectType()
    {
        // Arrange
        var page = new PageParameter();
        var complexState = new { Filter = "active", UserId = 123, Tags = new[] { "tag1", "tag2" } };

        // Act
        page.State = complexState;

        // Assert
        Assert.Equal(complexState, page.State);
    }
    #endregion

    #region 分页模式测试
    [Fact(DisplayName = "StartRow模式应正确设置")]
    public void StartRowMode_ShouldSetCorrectly()
    {
        // Arrange
        var page = new PageParameter
        {
            PageIndex = 5,
            StartRow = 100
        };

        // Act & Assert
        Assert.Equal(5, page.PageIndex); // PageIndex仍然保留
        Assert.Equal(100, page.StartRow); // StartRow应该设置
    }

    [Fact(DisplayName = "不同分页模式的有效性验证")]
    public void DifferentPagingModes_ValidityCheck()
    {
        // Arrange & Act & Assert - 正常PageIndex模式
        var pageIndexMode = new PageParameter { PageIndex = 2, PageSize = 20 };
        Assert.True(pageIndexMode.IsValid());

        // Arrange & Act & Assert - StartRow模式
        var startRowMode = new PageParameter { PageIndex = 1, PageSize = 20, StartRow = 40 };
        Assert.True(startRowMode.IsValid());

        // Arrange & Act & Assert - 不分页模式
        var noPagingMode = new PageParameter { PageIndex = 1, PageSize = 0 };
        Assert.True(noPagingMode.IsValid());
    }
    #endregion

    #region 排序优先级测试
    [Fact(DisplayName = "OrderBy优先级应高于Sort")]
    public void OrderBy_ShouldHaveHigherPriorityThanSort()
    {
        // Arrange
        var page = new PageParameter
        {
            Sort = "Name",
            OrderBy = "Custom ORDER BY"
        };

        // Act - 设置Sort应清空OrderBy
        page.Sort = "Age";

        // Assert
        Assert.Equal("Age", page.Sort);
        Assert.Null(page.OrderBy);

        // Act - 重新设置OrderBy
        page.OrderBy = "Another Custom ORDER BY";

        // Assert
        Assert.Equal("Age", page.Sort); // Sort保持不变
        Assert.Equal("Another Custom ORDER BY", page.OrderBy);
    }
    #endregion

    #region 扩展功能测试
    [Fact(DisplayName = "应正确处理扩展查询标志")]
    public void ExtendedQuery_FlagsHandling()
    {
        // Arrange
        var page = new PageParameter();

        // Act & Assert - 默认状态
        Assert.False(page.RetrieveTotalCount);
        Assert.False(page.RetrieveState);

        // Act & Assert - 设置标志
        page.RetrieveTotalCount = true;
        page.RetrieveState = true;
        Assert.True(page.RetrieveTotalCount);
        Assert.True(page.RetrieveState);
    }

    [Fact(DisplayName = "应正确处理数据权限State")]
    public void DataPermission_StateHandling()
    {
        // Arrange
        var page = new PageParameter();
        var permissionData = new
        {
            UserId = 123,
            Roles = new[] { "Admin", "User" },
            Filters = new Dictionary<String, Object>
            {
                ["Department"] = "IT",
                ["Level"] = 5
            }
        };

        // Act
        page.State = permissionData;
        page.RetrieveState = true;

        // Assert
        Assert.Equal(permissionData, page.State);
        Assert.True(page.RetrieveState);
    }

    [Fact(DisplayName = "测试Sort设置为null时的行为")]
    public void Sort_SetToNull_ShouldClearOrderBy()
    {
        // Arrange
        var page = new PageParameter();
        page.OrderBy = "Custom ORDER BY";

        // Act - 设置Sort为null也会触发setter
        page.Sort = null;

        // Assert
        Assert.Null(page.Sort);
        Assert.Null(page.OrderBy); // OrderBy应该被清空
    }
    #endregion
}