using FsCheck; using FsCheck.Xunit; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MiAssessment.Admin.Business.Data; using MiAssessment.Admin.Business.Entities; using MiAssessment.Admin.Business.Models.User; using MiAssessment.Admin.Business.Services; using Moq; using Xunit; namespace MiAssessment.Tests.Admin; /// /// User 属性测试 /// 验证用户服务的正确性属性 /// public class UserPropertyTests { private readonly Mock> _mockLogger = new(); #region Property 3: Pagination Correctness /// /// Property 3: 分页查询返回的记录数不超过 PageSize /// *For any* paginated list query with PageIndex and PageSize parameters, /// the result SHALL contain at most PageSize items. /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 100)] public bool PaginationCorrectness_ResultCountNotExceedPageSize(PositiveInt seed, PositiveInt pageSize) { // Arrange: 创建包含多个用户的数据库 using var dbContext = CreateDbContext(); var userCount = (seed.Get % 50) + 1; // 1-50 个用户 var actualPageSize = Math.Min(pageSize.Get, 100); // PageSize 最大 100 CreateTestUsers(dbContext, userCount, seed.Get); var service = new UserBusinessService(dbContext, _mockLogger.Object); // Act: 执行分页查询 var request = new UserQueryRequest { Page = 1, PageSize = actualPageSize }; var result = service.GetUserListAsync(request).GetAwaiter().GetResult(); // Assert: 返回的记录数不超过 PageSize return result.List.Count <= actualPageSize; } /// /// Property 3: Total 等于数据库中匹配记录的实际数量 /// *For any* paginated list query, the Total count SHALL equal /// the actual number of matching records in the database. /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 100)] public bool PaginationCorrectness_TotalEqualsActualCount(PositiveInt seed) { // Arrange: 创建包含多个用户的数据库 using var dbContext = CreateDbContext(); var userCount = (seed.Get % 50) + 1; // 1-50 个用户 CreateTestUsers(dbContext, userCount, seed.Get); var service = new UserBusinessService(dbContext, _mockLogger.Object); // 计算数据库中实际的非删除用户数量 var actualCount = dbContext.Users.Count(u => !u.IsDeleted); // Act: 执行分页查询 var request = new UserQueryRequest { Page = 1, PageSize = 10 }; var result = service.GetUserListAsync(request).GetAwaiter().GetResult(); // Assert: Total 等于实际数量 return result.Total == actualCount; } /// /// Property 3: 分页查询在不同页码下返回正确的记录数 /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 100)] public bool PaginationCorrectness_DifferentPagesReturnCorrectCount(PositiveInt seed) { // Arrange: 创建固定数量的用户 using var dbContext = CreateDbContext(); var userCount = 25; // 固定 25 个用户 var pageSize = 10; CreateTestUsers(dbContext, userCount, seed.Get); var service = new UserBusinessService(dbContext, _mockLogger.Object); // Act: 查询第一页 var request1 = new UserQueryRequest { Page = 1, PageSize = pageSize }; var result1 = service.GetUserListAsync(request1).GetAwaiter().GetResult(); // 查询第二页 var request2 = new UserQueryRequest { Page = 2, PageSize = pageSize }; var result2 = service.GetUserListAsync(request2).GetAwaiter().GetResult(); // 查询第三页 var request3 = new UserQueryRequest { Page = 3, PageSize = pageSize }; var result3 = service.GetUserListAsync(request3).GetAwaiter().GetResult(); // Assert: // 第一页应该有 10 条记录 // 第二页应该有 10 条记录 // 第三页应该有 5 条记录 return result1.List.Count == 10 && result2.List.Count == 10 && result3.List.Count == 5 && result1.Total == 25 && result2.Total == 25 && result3.Total == 25; } /// /// Property 3: 超出范围的页码返回空列表 /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 50)] public bool PaginationCorrectness_OutOfRangePageReturnsEmpty(PositiveInt seed) { // Arrange: 创建少量用户 using var dbContext = CreateDbContext(); var userCount = 5; CreateTestUsers(dbContext, userCount, seed.Get); var service = new UserBusinessService(dbContext, _mockLogger.Object); // Act: 查询超出范围的页码 var request = new UserQueryRequest { Page = 100, PageSize = 10 }; var result = service.GetUserListAsync(request).GetAwaiter().GetResult(); // Assert: 返回空列表,但 Total 仍然正确 return result.List.Count == 0 && result.Total == userCount; } /// /// Property 3: 带筛选条件的分页查询 Total 正确 /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 100)] public bool PaginationCorrectness_FilteredQueryTotalCorrect(PositiveInt seed) { // Arrange: 创建不同状态的用户 using var dbContext = CreateDbContext(); var activeCount = (seed.Get % 10) + 1; var disabledCount = (seed.Get % 5) + 1; // 创建启用状态的用户 for (int i = 0; i < activeCount; i++) { CreateTestUserWithStatus(dbContext, seed.Get + i, 1); } // 创建禁用状态的用户 for (int i = 0; i < disabledCount; i++) { CreateTestUserWithStatus(dbContext, seed.Get + activeCount + i, 0); } var service = new UserBusinessService(dbContext, _mockLogger.Object); // Act: 查询启用状态的用户 var request = new UserQueryRequest { Page = 1, PageSize = 100, Status = 1 }; var result = service.GetUserListAsync(request).GetAwaiter().GetResult(); // Assert: Total 等于启用状态的用户数量 return result.Total == activeCount; } /// /// Property 3: 带用户等级筛选的分页查询 Total 正确 /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 100)] public bool PaginationCorrectness_UserLevelFilterTotalCorrect(PositiveInt seed) { // Arrange: 创建不同等级的用户 using var dbContext = CreateDbContext(); var normalCount = (seed.Get % 10) + 1; var partnerCount = (seed.Get % 5) + 1; // 创建普通用户 for (int i = 0; i < normalCount; i++) { CreateTestUserWithLevel(dbContext, seed.Get + i, 1); } // 创建合伙人 for (int i = 0; i < partnerCount; i++) { CreateTestUserWithLevel(dbContext, seed.Get + normalCount + i, 2); } var service = new UserBusinessService(dbContext, _mockLogger.Object); // Act: 查询合伙人 var request = new UserQueryRequest { Page = 1, PageSize = 100, UserLevel = 2 }; var result = service.GetUserListAsync(request).GetAwaiter().GetResult(); // Assert: Total 等于合伙人数量 return result.Total == partnerCount; } /// /// Property 3: 软删除的用户不计入 Total /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 100)] public bool PaginationCorrectness_DeletedUsersNotInTotal(PositiveInt seed) { // Arrange: 创建用户,部分软删除 using var dbContext = CreateDbContext(); var activeCount = (seed.Get % 10) + 1; var deletedCount = (seed.Get % 5) + 1; // 创建正常用户 for (int i = 0; i < activeCount; i++) { CreateTestUser(dbContext, seed.Get + i, false); } // 创建已删除用户 for (int i = 0; i < deletedCount; i++) { CreateTestUser(dbContext, seed.Get + activeCount + i, true); } var service = new UserBusinessService(dbContext, _mockLogger.Object); // Act: 查询用户列表 var request = new UserQueryRequest { Page = 1, PageSize = 100 }; var result = service.GetUserListAsync(request).GetAwaiter().GetResult(); // Assert: Total 只包含未删除的用户 return result.Total == activeCount; } /// /// Property 3: TotalPages 计算正确 /// /// **Validates: Requirements 9.1** /// [Property(MaxTest = 100)] public bool PaginationCorrectness_TotalPagesCalculatedCorrectly(PositiveInt seed, PositiveInt pageSize) { // Arrange using var dbContext = CreateDbContext(); var userCount = (seed.Get % 100) + 1; var actualPageSize = Math.Max(1, Math.Min(pageSize.Get, 100)); CreateTestUsers(dbContext, userCount, seed.Get); var service = new UserBusinessService(dbContext, _mockLogger.Object); // Act var request = new UserQueryRequest { Page = 1, PageSize = actualPageSize }; var result = service.GetUserListAsync(request).GetAwaiter().GetResult(); // Assert: TotalPages = ceil(Total / PageSize) var expectedTotalPages = (int)Math.Ceiling((double)result.Total / actualPageSize); return result.TotalPages == expectedTotalPages; } #endregion #region 辅助方法 /// /// 创建内存数据库上下文 /// private AdminBusinessDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; return new AdminBusinessDbContext(options); } /// /// 创建多个测试用户 /// private void CreateTestUsers(AdminBusinessDbContext dbContext, int count, int seed) { for (int i = 0; i < count; i++) { CreateTestUser(dbContext, seed + i, false); } } /// /// 创建测试用户 /// private long CreateTestUser(AdminBusinessDbContext dbContext, int seed, bool isDeleted) { var user = new User { Uid = $"{seed % 1000000:D6}", OpenId = $"openid_{seed}", Phone = $"138{seed % 100000000:D8}", Nickname = $"User_{seed}", Avatar = $"https://example.com/avatar_{seed}.jpg", UserLevel = 1, Balance = seed % 1000, TotalIncome = seed % 5000, WithdrawnAmount = seed % 500, Status = 1, CreateTime = DateTime.Now.AddDays(-seed % 365), UpdateTime = DateTime.Now, IsDeleted = isDeleted }; dbContext.Users.Add(user); dbContext.SaveChanges(); return user.Id; } /// /// 创建指定状态的测试用户 /// private long CreateTestUserWithStatus(AdminBusinessDbContext dbContext, int seed, int status) { var user = new User { Uid = $"{seed % 1000000:D6}", OpenId = $"openid_{seed}", Phone = $"138{seed % 100000000:D8}", Nickname = $"User_{seed}", Avatar = $"https://example.com/avatar_{seed}.jpg", UserLevel = 1, Balance = seed % 1000, TotalIncome = seed % 5000, WithdrawnAmount = seed % 500, Status = status, CreateTime = DateTime.Now.AddDays(-seed % 365), UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.Users.Add(user); dbContext.SaveChanges(); return user.Id; } /// /// 创建指定等级的测试用户 /// private long CreateTestUserWithLevel(AdminBusinessDbContext dbContext, int seed, int userLevel) { var user = new User { Uid = $"{seed % 1000000:D6}", OpenId = $"openid_{seed}", Phone = $"138{seed % 100000000:D8}", Nickname = $"User_{seed}", Avatar = $"https://example.com/avatar_{seed}.jpg", UserLevel = userLevel, Balance = seed % 1000, TotalIncome = seed % 5000, WithdrawnAmount = seed % 500, Status = 1, CreateTime = DateTime.Now.AddDays(-seed % 365), UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.Users.Add(user); dbContext.SaveChanges(); return user.Id; } #endregion }