using FsCheck;
using FsCheck.Xunit;
using Xunit;
using XiangYi.Application.Services;
using XiangYi.Core.Entities.Biz;
namespace XiangYi.Application.Tests.Services;
///
/// RecommendService属性测试 - 推荐数量与会员等级
///
public class RecommendCountPropertyTests
{
///
/// **Feature: backend-api, Property 5: 推荐数量与会员等级关联**
/// **Validates: Requirements 3.1, 7.4**
///
/// *For any* 用户请求推荐列表, 普通用户返回不超过10人,不限时会员返回24人,诚意会员返回24-29人
///
[Property(MaxTest = 100)]
public Property RecommendCount_ShouldMatchMemberLevel()
{
// 生成有效的会员等级(0-4)
var memberLevelArb = Gen.Choose(0, 4);
return Prop.ForAll(
memberLevelArb.ToArbitrary(),
memberLevel =>
{
// Act
var (minCount, maxCount) = RecommendService.GetRecommendCountRangeByMemberLevel(memberLevel);
var actualCount = RecommendService.GetRecommendCountByMemberLevelStatic(memberLevel);
// Assert
return memberLevel switch
{
// 普通用户:恰好4人
0 => actualCount == RecommendService.NormalUserRecommendCount &&
minCount == 4 && maxCount == 4,
// 不限时会员:恰好24人
1 => actualCount == RecommendService.UnlimitedMemberRecommendCount &&
minCount == 24 && maxCount == 24,
// 诚意会员:24-29人
2 => actualCount >= RecommendService.SincereMemberMinRecommendCount &&
actualCount <= RecommendService.SincereMemberMaxRecommendCount &&
minCount == 24 && maxCount == 29,
// 家庭版会员:24-29人
3 => actualCount >= RecommendService.SincereMemberMinRecommendCount &&
actualCount <= RecommendService.SincereMemberMaxRecommendCount &&
minCount == 24 && maxCount == 29,
// 限时会员:恰好24人
4 => actualCount == RecommendService.UnlimitedMemberRecommendCount &&
minCount == 24 && maxCount == 24,
_ => false
};
});
}
///
/// 普通用户推荐数量应为4
///
[Property(MaxTest = 100)]
public Property NormalUser_ShouldGet10Recommendations()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
_ =>
{
var count = RecommendService.GetRecommendCountByMemberLevelStatic(0);
return count == 4;
});
}
///
/// 不限时会员推荐数量应为24
///
[Property(MaxTest = 100)]
public Property UnlimitedMember_ShouldGet24Recommendations()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
_ =>
{
var count = RecommendService.GetRecommendCountByMemberLevelStatic(1);
return count == 24;
});
}
///
/// 诚意会员推荐数量应在24-29之间
///
[Property(MaxTest = 100)]
public Property SincereMember_ShouldGet24To29Recommendations()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
_ =>
{
var count = RecommendService.GetRecommendCountByMemberLevelStatic(2);
return count >= 24 && count <= 29;
});
}
///
/// 家庭版会员推荐数量应在24-29之间
///
[Property(MaxTest = 100)]
public Property FamilyMember_ShouldGet24Recommendations()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
_ =>
{
var count = RecommendService.GetRecommendCountByMemberLevelStatic(3);
return count >= 24 && count <= 29;
});
}
///
/// 无效会员等级应返回普通用户数量
///
[Property(MaxTest = 100)]
public Property InvalidMemberLevel_ShouldReturnNormalUserCount()
{
// 生成无效的会员等级(负数或大于4)
var invalidLevelArb = Gen.OneOf(
Gen.Choose(-100, -1),
Gen.Choose(5, 100)
);
return Prop.ForAll(
invalidLevelArb.ToArbitrary(),
invalidLevel =>
{
var count = RecommendService.GetRecommendCountByMemberLevelStatic(invalidLevel);
return count == RecommendService.NormalUserRecommendCount;
});
}
///
/// 会员推荐数量应大于等于普通用户
///
[Property(MaxTest = 100)]
public Property MemberRecommendCount_ShouldBeGreaterOrEqualToNormalUser()
{
var memberLevelArb = Gen.Choose(1, 4); // 会员等级1-4
return Prop.ForAll(
memberLevelArb.ToArbitrary(),
memberLevel =>
{
var memberCount = RecommendService.GetRecommendCountByMemberLevelStatic(memberLevel);
var normalCount = RecommendService.GetRecommendCountByMemberLevelStatic(0);
return memberCount >= normalCount;
});
}
///
/// 常量值验证
///
[Fact]
public void Constants_ShouldHaveCorrectValues()
{
Assert.Equal(4, RecommendService.NormalUserRecommendCount);
Assert.Equal(24, RecommendService.UnlimitedMemberRecommendCount);
Assert.Equal(24, RecommendService.SincereMemberMinRecommendCount);
Assert.Equal(29, RecommendService.SincereMemberMaxRecommendCount);
Assert.Equal(24, RecommendService.FamilyMemberRecommendCount);
Assert.Equal(3, RecommendService.DeduplicationDays);
Assert.Equal(30, RecommendService.NewUserPriorityMinutes);
}
}
///
/// RecommendService属性测试 - 推荐去重规则
///
public class RecommendDeduplicationPropertyTests
{
///
/// **Feature: backend-api, Property 6: 推荐去重规则**
/// **Validates: Requirements 3.3**
///
/// *For any* 生成的推荐列表, 3天内已推荐过的用户不应再次出现(用户数量充足时)
///
[Property(MaxTest = 100)]
public Property RecommendDeduplication_ShouldExcludeRecentlyRecommended()
{
// 生成用户ID
var userIdArb = Gen.Choose(1, 1000);
var recommendUserIdArb = Gen.Choose(1001, 2000);
// 生成推荐日期(0-10天前)
var daysAgoArb = Gen.Choose(0, 10);
return Prop.ForAll(
userIdArb.ToArbitrary(),
recommendUserIdArb.ToArbitrary(),
daysAgoArb.ToArbitrary(),
(userId, recommendUserId, daysAgo) =>
{
// Arrange - 创建模拟的推荐记录
var existingRecommends = new List
{
new DailyRecommend
{
UserId = userId,
RecommendUserId = recommendUserId,
RecommendDate = DateTime.Today.AddDays(-daysAgo),
CreateTime = DateTime.Now.AddDays(-daysAgo)
}
};
// Act
var isRecentlyRecommended = RecommendService.IsRecommendedInDaysStatic(
existingRecommends,
userId,
recommendUserId,
RecommendService.DeduplicationDays);
// Assert
// 如果推荐日期在3天内(包括今天),应该返回true
// 如果推荐日期超过3天,应该返回false
var expectedResult = daysAgo <= RecommendService.DeduplicationDays;
return isRecentlyRecommended == expectedResult;
});
}
///
/// 今日推荐的用户应被标记为已推荐
///
[Property(MaxTest = 100)]
public Property TodayRecommend_ShouldBeMarkedAsRecent()
{
var userIdArb = Gen.Choose(1, 1000);
var recommendUserIdArb = Gen.Choose(1001, 2000);
return Prop.ForAll(
userIdArb.ToArbitrary(),
recommendUserIdArb.ToArbitrary(),
(userId, recommendUserId) =>
{
// Arrange - 今日推荐
var existingRecommends = new List
{
new DailyRecommend
{
UserId = userId,
RecommendUserId = recommendUserId,
RecommendDate = DateTime.Today,
CreateTime = DateTime.Now
}
};
// Act
var isRecentlyRecommended = RecommendService.IsRecommendedInDaysStatic(
existingRecommends,
userId,
recommendUserId,
RecommendService.DeduplicationDays);
// Assert - 今日推荐应该被标记为已推荐
return isRecentlyRecommended;
});
}
///
/// 3天前推荐的用户应被标记为已推荐
///
[Property(MaxTest = 100)]
public Property ThreeDaysAgoRecommend_ShouldBeMarkedAsRecent()
{
var userIdArb = Gen.Choose(1, 1000);
var recommendUserIdArb = Gen.Choose(1001, 2000);
return Prop.ForAll(
userIdArb.ToArbitrary(),
recommendUserIdArb.ToArbitrary(),
(userId, recommendUserId) =>
{
// Arrange - 3天前推荐
var existingRecommends = new List
{
new DailyRecommend
{
UserId = userId,
RecommendUserId = recommendUserId,
RecommendDate = DateTime.Today.AddDays(-3),
CreateTime = DateTime.Now.AddDays(-3)
}
};
// Act
var isRecentlyRecommended = RecommendService.IsRecommendedInDaysStatic(
existingRecommends,
userId,
recommendUserId,
RecommendService.DeduplicationDays);
// Assert - 3天前推荐应该被标记为已推荐(边界情况)
return isRecentlyRecommended;
});
}
///
/// 4天前推荐的用户不应被标记为已推荐
///
[Property(MaxTest = 100)]
public Property FourDaysAgoRecommend_ShouldNotBeMarkedAsRecent()
{
var userIdArb = Gen.Choose(1, 1000);
var recommendUserIdArb = Gen.Choose(1001, 2000);
return Prop.ForAll(
userIdArb.ToArbitrary(),
recommendUserIdArb.ToArbitrary(),
(userId, recommendUserId) =>
{
// Arrange - 4天前推荐
var existingRecommends = new List
{
new DailyRecommend
{
UserId = userId,
RecommendUserId = recommendUserId,
RecommendDate = DateTime.Today.AddDays(-4),
CreateTime = DateTime.Now.AddDays(-4)
}
};
// Act
var isRecentlyRecommended = RecommendService.IsRecommendedInDaysStatic(
existingRecommends,
userId,
recommendUserId,
RecommendService.DeduplicationDays);
// Assert - 4天前推荐不应该被标记为已推荐
return !isRecentlyRecommended;
});
}
///
/// 不同用户的推荐记录不应影响当前用户
///
[Property(MaxTest = 100)]
public Property DifferentUserRecommend_ShouldNotAffectCurrentUser()
{
var userIdArb = Gen.Choose(1, 500);
var otherUserIdArb = Gen.Choose(501, 1000);
var recommendUserIdArb = Gen.Choose(1001, 2000);
return Prop.ForAll(
userIdArb.ToArbitrary(),
otherUserIdArb.ToArbitrary(),
recommendUserIdArb.ToArbitrary(),
(userId, otherUserId, recommendUserId) =>
{
// Arrange - 其他用户的推荐记录
var existingRecommends = new List
{
new DailyRecommend
{
UserId = otherUserId, // 不同的用户
RecommendUserId = recommendUserId,
RecommendDate = DateTime.Today,
CreateTime = DateTime.Now
}
};
// Act
var isRecentlyRecommended = RecommendService.IsRecommendedInDaysStatic(
existingRecommends,
userId, // 当前用户
recommendUserId,
RecommendService.DeduplicationDays);
// Assert - 其他用户的推荐记录不应影响当前用户
return !isRecentlyRecommended;
});
}
///
/// 空推荐列表应返回未推荐
///
[Property(MaxTest = 100)]
public Property EmptyRecommendList_ShouldReturnNotRecommended()
{
var userIdArb = Gen.Choose(1, 1000);
var recommendUserIdArb = Gen.Choose(1001, 2000);
return Prop.ForAll(
userIdArb.ToArbitrary(),
recommendUserIdArb.ToArbitrary(),
(userId, recommendUserId) =>
{
// Arrange - 空推荐列表
var existingRecommends = new List();
// Act
var isRecentlyRecommended = RecommendService.IsRecommendedInDaysStatic(
existingRecommends,
userId,
recommendUserId,
RecommendService.DeduplicationDays);
// Assert - 空列表应返回未推荐
return !isRecentlyRecommended;
});
}
///
/// 去重天数常量应为3
///
[Fact]
public void DeduplicationDays_ShouldBe3()
{
Assert.Equal(3, RecommendService.DeduplicationDays);
}
}