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
}