425 lines
15 KiB
C#
425 lines
15 KiB
C#
using FsCheck;
|
||
using FsCheck.Xunit;
|
||
using Xunit;
|
||
using XiangYi.Application.Services;
|
||
using XiangYi.Core.Entities.Biz;
|
||
|
||
namespace XiangYi.Application.Tests.Services;
|
||
|
||
/// <summary>
|
||
/// RecommendService属性测试 - 推荐数量与会员等级
|
||
/// </summary>
|
||
public class RecommendCountPropertyTests
|
||
{
|
||
/// <summary>
|
||
/// **Feature: backend-api, Property 5: 推荐数量与会员等级关联**
|
||
/// **Validates: Requirements 3.1, 7.4**
|
||
///
|
||
/// *For any* 用户请求推荐列表, 普通用户返回不超过10人,不限时会员返回24人,诚意会员返回24-29人
|
||
/// </summary>
|
||
[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
|
||
};
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 普通用户推荐数量应为4
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public Property NormalUser_ShouldGet10Recommendations()
|
||
{
|
||
return Prop.ForAll(
|
||
Arb.Default.PositiveInt(),
|
||
_ =>
|
||
{
|
||
var count = RecommendService.GetRecommendCountByMemberLevelStatic(0);
|
||
return count == 4;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 不限时会员推荐数量应为24
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public Property UnlimitedMember_ShouldGet24Recommendations()
|
||
{
|
||
return Prop.ForAll(
|
||
Arb.Default.PositiveInt(),
|
||
_ =>
|
||
{
|
||
var count = RecommendService.GetRecommendCountByMemberLevelStatic(1);
|
||
return count == 24;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 诚意会员推荐数量应在24-29之间
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public Property SincereMember_ShouldGet24To29Recommendations()
|
||
{
|
||
return Prop.ForAll(
|
||
Arb.Default.PositiveInt(),
|
||
_ =>
|
||
{
|
||
var count = RecommendService.GetRecommendCountByMemberLevelStatic(2);
|
||
return count >= 24 && count <= 29;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 家庭版会员推荐数量应在24-29之间
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public Property FamilyMember_ShouldGet24Recommendations()
|
||
{
|
||
return Prop.ForAll(
|
||
Arb.Default.PositiveInt(),
|
||
_ =>
|
||
{
|
||
var count = RecommendService.GetRecommendCountByMemberLevelStatic(3);
|
||
return count >= 24 && count <= 29;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 无效会员等级应返回普通用户数量
|
||
/// </summary>
|
||
[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;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 会员推荐数量应大于等于普通用户
|
||
/// </summary>
|
||
[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;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 常量值验证
|
||
/// </summary>
|
||
[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);
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// RecommendService属性测试 - 推荐去重规则
|
||
/// </summary>
|
||
public class RecommendDeduplicationPropertyTests
|
||
{
|
||
/// <summary>
|
||
/// **Feature: backend-api, Property 6: 推荐去重规则**
|
||
/// **Validates: Requirements 3.3**
|
||
///
|
||
/// *For any* 生成的推荐列表, 3天内已推荐过的用户不应再次出现(用户数量充足时)
|
||
/// </summary>
|
||
[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<DailyRecommend>
|
||
{
|
||
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;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 今日推荐的用户应被标记为已推荐
|
||
/// </summary>
|
||
[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<DailyRecommend>
|
||
{
|
||
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;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 3天前推荐的用户应被标记为已推荐
|
||
/// </summary>
|
||
[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<DailyRecommend>
|
||
{
|
||
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;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 4天前推荐的用户不应被标记为已推荐
|
||
/// </summary>
|
||
[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<DailyRecommend>
|
||
{
|
||
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;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 不同用户的推荐记录不应影响当前用户
|
||
/// </summary>
|
||
[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<DailyRecommend>
|
||
{
|
||
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;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 空推荐列表应返回未推荐
|
||
/// </summary>
|
||
[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<DailyRecommend>();
|
||
|
||
// Act
|
||
var isRecentlyRecommended = RecommendService.IsRecommendedInDaysStatic(
|
||
existingRecommends,
|
||
userId,
|
||
recommendUserId,
|
||
RecommendService.DeduplicationDays);
|
||
|
||
// Assert - 空列表应返回未推荐
|
||
return !isRecentlyRecommended;
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 去重天数常量应为3
|
||
/// </summary>
|
||
[Fact]
|
||
public void DeduplicationDays_ShouldBe3()
|
||
{
|
||
Assert.Equal(3, RecommendService.DeduplicationDays);
|
||
}
|
||
}
|