xiangyixiangqin/server/tests/XiangYi.Application.Tests/Services/RecommendServicePropertyTests.cs
2026-02-28 03:49:11 +08:00

425 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}