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.Distribution; using MiAssessment.Admin.Business.Services; using Moq; using Xunit; namespace MiAssessment.Tests.Admin; /// /// Commission 属性测试 /// 验证佣金服务的正确性属性 /// public class CommissionPropertyTests { private readonly Mock> _mockLogger = new(); #region Property 14: Commission Statistics Accuracy /// /// Property 14: 佣金统计 - TotalAmount 等于所有 CommissionAmount 的总和 /// *For any* commission statistics query, the TotalAmount SHALL equal /// the sum of all CommissionAmount values. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_TotalAmountEqualsSum(PositiveInt seed, PositiveInt count) { // Arrange: 创建多条佣金记录 using var dbContext = CreateDbContext(); var recordCount = (count.Get % 10) + 1; // 1-10条记录 var expectedTotal = 0m; var userId = CreateTestUser(dbContext, seed.Get); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId); for (int i = 0; i < recordCount; i++) { var amount = ((seed.Get + i) % 100) + 1m; // 1-100 CreateTestCommission(dbContext, seed.Get + i, userId, fromUserId, orderId, amount, status: (i % 2) + 1); expectedTotal += amount; } var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: TotalAmount 应该等于所有 CommissionAmount 的总和 return statistics.TotalAmount == expectedTotal; } /// /// Property 14: 佣金统计 - PendingAmount 等于状态为1的 CommissionAmount 总和 /// *For any* commission statistics query, the PendingAmount SHALL equal /// the sum of CommissionAmount where Status is 1. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_PendingAmountEqualsStatusOneSum(PositiveInt seed, PositiveInt count) { // Arrange: 创建多条佣金记录,混合状态 using var dbContext = CreateDbContext(); var recordCount = (count.Get % 10) + 1; // 1-10条记录 var expectedPending = 0m; var userId = CreateTestUser(dbContext, seed.Get); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId); for (int i = 0; i < recordCount; i++) { var amount = ((seed.Get + i) % 100) + 1m; // 1-100 var status = (i % 2) + 1; // 交替 1 和 2 CreateTestCommission(dbContext, seed.Get + i, userId, fromUserId, orderId, amount, status: status); if (status == 1) { expectedPending += amount; } } var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: PendingAmount 应该等于状态为1的 CommissionAmount 总和 return statistics.PendingAmount == expectedPending; } /// /// Property 14: 佣金统计 - SettledAmount 等于状态为2的 CommissionAmount 总和 /// *For any* commission statistics query, the SettledAmount SHALL equal /// the sum of CommissionAmount where Status is 2. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_SettledAmountEqualsStatusTwoSum(PositiveInt seed, PositiveInt count) { // Arrange: 创建多条佣金记录,混合状态 using var dbContext = CreateDbContext(); var recordCount = (count.Get % 10) + 1; // 1-10条记录 var expectedSettled = 0m; var userId = CreateTestUser(dbContext, seed.Get); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId); for (int i = 0; i < recordCount; i++) { var amount = ((seed.Get + i) % 100) + 1m; // 1-100 var status = (i % 2) + 1; // 交替 1 和 2 CreateTestCommission(dbContext, seed.Get + i, userId, fromUserId, orderId, amount, status: status); if (status == 2) { expectedSettled += amount; } } var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: SettledAmount 应该等于状态为2的 CommissionAmount 总和 return statistics.SettledAmount == expectedSettled; } /// /// Property 14: 佣金统计 - TotalAmount 等于 PendingAmount + SettledAmount /// *For any* commission statistics query, TotalAmount SHALL equal /// PendingAmount + SettledAmount. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_TotalEqualsPendingPlusSettled(PositiveInt seed, PositiveInt count) { // Arrange: 创建多条佣金记录 using var dbContext = CreateDbContext(); var recordCount = (count.Get % 10) + 1; // 1-10条记录 var userId = CreateTestUser(dbContext, seed.Get); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId); for (int i = 0; i < recordCount; i++) { var amount = ((seed.Get + i) % 100) + 1m; // 1-100 var status = (i % 2) + 1; // 交替 1 和 2 CreateTestCommission(dbContext, seed.Get + i, userId, fromUserId, orderId, amount, status: status); } var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: TotalAmount 应该等于 PendingAmount + SettledAmount return statistics.TotalAmount == statistics.PendingAmount + statistics.SettledAmount; } /// /// Property 14: 佣金统计 - TotalCount 等于所有记录数 /// *For any* commission statistics query, TotalCount SHALL equal /// the count of all commission records. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_TotalCountEqualsRecordCount(PositiveInt seed, PositiveInt count) { // Arrange: 创建多条佣金记录 using var dbContext = CreateDbContext(); var recordCount = (count.Get % 10) + 1; // 1-10条记录 var userId = CreateTestUser(dbContext, seed.Get); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId); for (int i = 0; i < recordCount; i++) { var amount = ((seed.Get + i) % 100) + 1m; var status = (i % 2) + 1; CreateTestCommission(dbContext, seed.Get + i, userId, fromUserId, orderId, amount, status: status); } var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: TotalCount 应该等于记录数 return statistics.TotalCount == recordCount; } /// /// Property 14: 佣金统计 - TotalCount 等于 PendingCount + SettledCount /// *For any* commission statistics query, TotalCount SHALL equal /// PendingCount + SettledCount. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_TotalCountEqualsPendingPlusSettledCount(PositiveInt seed, PositiveInt count) { // Arrange: 创建多条佣金记录 using var dbContext = CreateDbContext(); var recordCount = (count.Get % 10) + 1; // 1-10条记录 var userId = CreateTestUser(dbContext, seed.Get); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId); for (int i = 0; i < recordCount; i++) { var amount = ((seed.Get + i) % 100) + 1m; var status = (i % 2) + 1; CreateTestCommission(dbContext, seed.Get + i, userId, fromUserId, orderId, amount, status: status); } var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: TotalCount 应该等于 PendingCount + SettledCount return statistics.TotalCount == statistics.PendingCount + statistics.SettledCount; } /// /// Property 14: 佣金统计 - 按用户ID筛选时统计正确 /// *For any* commission statistics query with UserId filter, /// the statistics SHALL only include records for that user. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_FilterByUserIdCorrect(PositiveInt seed) { // Arrange: 创建两个用户的佣金记录 using var dbContext = CreateDbContext(); var userId1 = CreateTestUser(dbContext, seed.Get); var userId2 = CreateTestUser(dbContext, seed.Get + 500); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId1); // 用户1的佣金记录 var user1Amount1 = ((seed.Get) % 100) + 1m; var user1Amount2 = ((seed.Get + 1) % 100) + 1m; CreateTestCommission(dbContext, seed.Get, userId1, fromUserId, orderId, user1Amount1, status: 1); CreateTestCommission(dbContext, seed.Get + 1, userId1, fromUserId, orderId, user1Amount2, status: 2); // 用户2的佣金记录 var user2Amount = ((seed.Get + 2) % 100) + 1m; CreateTestCommission(dbContext, seed.Get + 2, userId2, fromUserId, orderId, user2Amount, status: 1); var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 按用户1筛选获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest { UserId = userId1 }).GetAwaiter().GetResult(); // Assert: 统计数据应该只包含用户1的记录 var expectedTotal = user1Amount1 + user1Amount2; return statistics.TotalAmount == expectedTotal && statistics.TotalCount == 2 && statistics.PendingAmount == user1Amount1 && statistics.SettledAmount == user1Amount2; } /// /// Property 14: 佣金统计 - 空数据时返回零值 /// *For any* commission statistics query with no matching records, /// all statistics values SHALL be zero. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 50)] public bool CommissionStatistics_EmptyDataReturnsZero(PositiveInt seed) { // Arrange: 创建空数据库 using var dbContext = CreateDbContext(); var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: 所有统计值应该为零 return statistics.TotalAmount == 0m && statistics.PendingAmount == 0m && statistics.SettledAmount == 0m && statistics.TotalCount == 0 && statistics.PendingCount == 0 && statistics.SettledCount == 0; } /// /// Property 14: 佣金统计 - 软删除记录不计入统计 /// *For any* commission statistics query, soft-deleted records /// SHALL NOT be included in the statistics. /// /// **Validates: Requirements 15.6** /// [Property(MaxTest = 100)] public bool CommissionStatistics_SoftDeletedNotIncluded(PositiveInt seed) { // Arrange: 创建佣金记录,部分软删除 using var dbContext = CreateDbContext(); var userId = CreateTestUser(dbContext, seed.Get); var fromUserId = CreateTestUser(dbContext, seed.Get + 1000); var orderId = CreateTestOrder(dbContext, seed.Get, userId); // 正常记录 var normalAmount = ((seed.Get) % 100) + 1m; CreateTestCommission(dbContext, seed.Get, userId, fromUserId, orderId, normalAmount, status: 1, isDeleted: false); // 软删除记录 var deletedAmount = ((seed.Get + 1) % 100) + 1m; CreateTestCommission(dbContext, seed.Get + 1, userId, fromUserId, orderId, deletedAmount, status: 1, isDeleted: true); var service = new DistributionService(dbContext, _mockLogger.Object); // Act: 获取统计数据 var statistics = service.GetCommissionStatisticsAsync(new CommissionStatisticsRequest()).GetAwaiter().GetResult(); // Assert: 统计数据应该只包含正常记录 return statistics.TotalAmount == normalAmount && statistics.TotalCount == 1; } #endregion #region 辅助方法 /// /// 创建内存数据库上下文 /// private AdminBusinessDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; return new AdminBusinessDbContext(options); } /// /// 创建测试用户 /// /// 数据库上下文 /// 随机种子 /// 用户ID private long CreateTestUser(AdminBusinessDbContext dbContext, int seed) { var user = new User { Uid = $"{seed % 1000000:D6}", OpenId = $"openid_{seed}", Phone = $"138{seed % 100000000:D8}", Nickname = $"User_{seed}", UserLevel = 1, Status = 1, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.Users.Add(user); dbContext.SaveChanges(); return user.Id; } /// /// 创建测试订单 /// /// 数据库上下文 /// 随机种子 /// 用户ID /// 订单ID private long CreateTestOrder(AdminBusinessDbContext dbContext, int seed, long userId) { var order = new Order { OrderNo = $"ORD{DateTime.Now:yyyyMMddHHmmss}{seed % 10000:D4}", UserId = userId, OrderType = (seed % 2) + 1, ProductId = seed % 100 + 1, ProductName = $"测试商品_{seed}", Amount = 100m, PayAmount = 100m, PayType = 1, Status = 2, PayTime = DateTime.Now.AddMinutes(-seed % 60), CreateTime = DateTime.Now.AddDays(-seed % 30), UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.Orders.Add(order); dbContext.SaveChanges(); return order.Id; } /// /// 创建测试佣金记录 /// /// 数据库上下文 /// 随机种子 /// 获得佣金的用户ID /// 来源用户ID /// 订单ID /// 佣金金额 /// 状态:1待结算 2已结算 /// 是否软删除 /// 佣金记录ID private long CreateTestCommission( AdminBusinessDbContext dbContext, int seed, long userId, long fromUserId, long orderId, decimal amount, int status = 1, bool isDeleted = false) { var commission = new Commission { UserId = userId, FromUserId = fromUserId, OrderId = orderId, OrderAmount = amount * 10, // 订单金额是佣金的10倍 CommissionRate = 0.1m, // 10%佣金比例 CommissionAmount = amount, Level = (seed % 2) + 1, // 1或2 Status = status, SettleTime = status == 2 ? DateTime.Now : null, CreateTime = DateTime.Now.AddDays(-seed % 30), UpdateTime = DateTime.Now, IsDeleted = isDeleted }; dbContext.Commissions.Add(commission); dbContext.SaveChanges(); return commission.Id; } #endregion }