using FsCheck;
using FsCheck.Xunit;
using Infrastructure;
using Microsoft.Extensions.Configuration;
using Xunit;
using ZR.Model.Business.Dto;
using ZR.Service.Business;
namespace ZR.Tests
{
///
/// 导出分页属性测试
/// **Feature: work-camera-2.0, Property 4: Export Pagination Consistency**
/// **Validates: Requirements 4.1, 4.2, 4.4**
///
public class ExportPaginationPropertyTests : IClassFixture
{
public ExportPaginationPropertyTests(TestFixture fixture)
{
// 确保配置已初始化
}
///
/// Property 4: Export Pagination Consistency - Page Size Capping
///
/// *For any* export query with page size greater than 50,
/// the system SHALL cap the page size to 50.
///
/// **Validates: Requirements 4.1, 4.2, 4.4**
///
[Property(MaxTest = 100)]
public Property PageSize_ShouldBeCappedAt50()
{
// Generate page sizes including values above 50
var pageSizeGen = Gen.Choose(1, 200);
return Prop.ForAll(
pageSizeGen.ToArbitrary(),
requestedPageSize =>
{
var query = new WorkRecordExportQueryDto
{
PageNum = 1,
PageSize = requestedPageSize
};
// Simulate the capping logic from WorkRecordExportService
var effectivePageSize = requestedPageSize > WorkRecordExportService.MaxPageSize
? WorkRecordExportService.MaxPageSize
: requestedPageSize;
// Verify: Effective page size should never exceed 50
if (effectivePageSize > 50)
return false.Label($"Effective page size {effectivePageSize} exceeds maximum 50");
// Verify: If requested <= 50, effective should equal requested
if (requestedPageSize <= 50 && effectivePageSize != requestedPageSize)
return false.Label($"Page size {requestedPageSize} should not be modified when <= 50");
// Verify: If requested > 50, effective should be 50
if (requestedPageSize > 50 && effectivePageSize != 50)
return false.Label($"Page size should be capped to 50 when requested {requestedPageSize}");
return true.Label("Page size capping works correctly");
});
}
///
/// Property 4: Export Pagination Consistency - Page Number Validation
///
/// *For any* export query, page number should be at least 1.
///
/// **Validates: Requirements 4.1, 4.2, 4.4**
///
[Property(MaxTest = 100)]
public Property PageNumber_ShouldBeAtLeast1()
{
// Generate page numbers including invalid values
var pageNumGen = Gen.Choose(-10, 100);
return Prop.ForAll(
pageNumGen.ToArbitrary(),
requestedPageNum =>
{
var query = new WorkRecordExportQueryDto
{
PageNum = requestedPageNum,
PageSize = 50
};
// Simulate the validation logic
var effectivePageNum = requestedPageNum <= 0 ? 1 : requestedPageNum;
// Verify: Effective page number should be at least 1
if (effectivePageNum < 1)
return false.Label($"Effective page number {effectivePageNum} should be at least 1");
// Verify: If requested > 0, effective should equal requested
if (requestedPageNum > 0 && effectivePageNum != requestedPageNum)
return false.Label($"Page number {requestedPageNum} should not be modified when > 0");
// Verify: If requested <= 0, effective should be 1
if (requestedPageNum <= 0 && effectivePageNum != 1)
return false.Label($"Page number should default to 1 when requested {requestedPageNum}");
return true.Label("Page number validation works correctly");
});
}
///
/// Property 4: Export Pagination Consistency - Pagination Calculation
///
/// *For any* total record count N and page size P (capped at 50),
/// the total number of pages should be ceil(N/P).
///
/// **Validates: Requirements 4.1, 4.2, 4.4**
///
[Property(MaxTest = 100)]
public Property TotalPages_ShouldBeCalculatedCorrectly()
{
// Generate total counts and page sizes
var paramsGen = from totalCount in Gen.Choose(0, 1000)
from pageSize in Gen.Choose(1, 100)
select new { totalCount, pageSize };
return Prop.ForAll(
paramsGen.ToArbitrary(),
p =>
{
// Cap page size at 50
var effectivePageSize = Math.Min(p.pageSize, WorkRecordExportService.MaxPageSize);
// Calculate expected total pages
var expectedTotalPages = p.totalCount == 0
? 0
: (int)Math.Ceiling((double)p.totalCount / effectivePageSize);
// Verify: Total pages calculation
if (p.totalCount == 0 && expectedTotalPages != 0)
return false.Label("Total pages should be 0 when no records");
if (p.totalCount > 0 && expectedTotalPages < 1)
return false.Label("Total pages should be at least 1 when records exist");
// Verify: All records can be retrieved by iterating through pages
var totalRecordsFromPages = 0;
for (int page = 1; page <= expectedTotalPages; page++)
{
var recordsInPage = page < expectedTotalPages
? effectivePageSize
: p.totalCount - (expectedTotalPages - 1) * effectivePageSize;
totalRecordsFromPages += recordsInPage;
}
if (totalRecordsFromPages != p.totalCount)
return false.Label($"Iterating through {expectedTotalPages} pages should yield {p.totalCount} records, got {totalRecordsFromPages}");
return true.Label("Pagination calculation is correct");
});
}
///
/// Property 4: Export Pagination Consistency - No Duplicate Records
///
/// *For any* pagination scenario, records on different pages should not overlap.
/// This is verified by checking that skip values are correctly calculated.
///
/// **Validates: Requirements 4.1, 4.2, 4.4**
///
[Property(MaxTest = 100)]
public Property PaginationSkip_ShouldNotCauseDuplicates()
{
// Generate pagination parameters
var paramsGen = from pageNum in Gen.Choose(1, 50)
from pageSize in Gen.Choose(1, 100)
select new { pageNum, pageSize };
return Prop.ForAll(
paramsGen.ToArbitrary(),
p =>
{
// Cap page size at 50
var effectivePageSize = Math.Min(p.pageSize, WorkRecordExportService.MaxPageSize);
// Calculate skip value for current page
var skip = (p.pageNum - 1) * effectivePageSize;
// Calculate skip value for next page
var nextPageSkip = p.pageNum * effectivePageSize;
// Verify: Skip values should not overlap
if (skip < 0)
return false.Label($"Skip value {skip} should not be negative");
// Verify: Next page skip should be exactly pageSize more than current
if (nextPageSkip - skip != effectivePageSize)
return false.Label($"Gap between pages should be {effectivePageSize}, got {nextPageSkip - skip}");
// Verify: Records from page N end where page N+1 begins
var pageEndIndex = skip + effectivePageSize - 1;
var nextPageStartIndex = nextPageSkip;
if (nextPageStartIndex != pageEndIndex + 1)
return false.Label($"Page {p.pageNum} ends at {pageEndIndex}, page {p.pageNum + 1} should start at {pageEndIndex + 1}");
return true.Label("Pagination skip values are correct");
});
}
///
/// Property 4: Export Pagination Consistency - Query DTO Default Values
///
/// *For any* WorkRecordExportQueryDto, default values should be:
/// - PageNum = 1
/// - PageSize = 50
///
/// **Validates: Requirements 4.1, 4.2, 4.4**
///
[Fact]
public void QueryDto_ShouldHaveCorrectDefaults()
{
var query = new WorkRecordExportQueryDto();
Assert.Equal(1, query.PageNum);
Assert.Equal(50, query.PageSize);
}
///
/// Property 4: Export Pagination Consistency - MaxPageSize Constant
///
/// The MaxPageSize constant should be 50 as per requirements.
///
/// **Validates: Requirements 4.1, 4.2, 4.4**
///
[Fact]
public void MaxPageSize_ShouldBe50()
{
Assert.Equal(50, WorkRecordExportService.MaxPageSize);
}
}
}