vending-machine/backend/tests/VendingMachine.Tests/MembershipPropertyTests.cs
2026-04-03 06:07:13 +08:00

138 lines
5.3 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 VendingMachine.Application.Services;
namespace VendingMachine.Tests;
/// <summary>
/// 会员与支付模块属性测试
/// </summary>
public class MembershipPropertyTests
{
// Feature: vending-machine-app, Property 7: 会员页按钮状态取决于会员类型
// 对于任意用户的会员类型none/monthly/subscription会员页按钮应遵循以下规则
// 无会员时显示开通按钮;已购单月会员时隐藏单月按钮仅显示订阅按钮;已购订阅会员时按钮置灰不可点击。
// **Validates: Requirements 4.3, 4.7, 4.8**
[Property(MaxTest = 100)]
public bool MembershipButtonState_None_ShowsBothButtons(int seed)
{
var state = IMembershipService.GetButtonState("none");
return state.ShowMonthlyButton
&& state.ShowSubscriptionButton
&& state.MonthlyButtonEnabled
&& state.SubscriptionButtonEnabled;
}
[Property(MaxTest = 100)]
public bool MembershipButtonState_Monthly_HidesMonthlyButton(int seed)
{
var state = IMembershipService.GetButtonState("monthly");
return !state.ShowMonthlyButton
&& state.ShowSubscriptionButton
&& !state.MonthlyButtonEnabled
&& state.SubscriptionButtonEnabled;
}
[Property(MaxTest = 100)]
public bool MembershipButtonState_Subscription_DisablesAllButtons(int seed)
{
var state = IMembershipService.GetButtonState("subscription");
return !state.MonthlyButtonEnabled
&& !state.SubscriptionButtonEnabled
&& state.SubscriptionButtonText == "已购买订阅会员";
}
// Feature: vending-machine-app, Property 8: 平台与支付方式映射
// 对于任意设备平台Android 应调用 Google PayiOS 应调用 Apple Pay。
// **Validates: Requirements 4.2**
[Property(MaxTest = 100)]
public bool PlatformPaymentMethod_Android_MapsToGooglePay(int seed)
{
return IMembershipService.GetPaymentMethod("android") == "Google Pay";
}
[Property(MaxTest = 100)]
public bool PlatformPaymentMethod_iOS_MapsToApplePay(int seed)
{
return IMembershipService.GetPaymentMethod("ios") == "Apple Pay";
}
// Feature: vending-machine-app, Property 9: 积分保留与清空规则
// 对于任意会员到期的用户如果当前时间距会员到期不超过3个月则积分保留超过3个月则积分清空为0。
// **Validates: Requirements 4.4, 4.5**
[Property(MaxTest = 100)]
public bool PointsRetention_WhenExpiredWithin3Months_ShouldRetain(PositiveInt daysExpired)
{
// 限制在1-89天内不超过3个月≈90天
var days = (daysExpired.Get % 89) + 1;
var expireDate = DateTime.UtcNow.AddDays(-days);
var now = DateTime.UtcNow;
// 到期不超过3个月积分应保留不清空
return !IMembershipService.ShouldClearPoints(expireDate, now);
}
[Property(MaxTest = 100)]
public bool PointsClear_WhenExpiredOver3Months_ShouldClear(PositiveInt extraDays)
{
// 到期超过3个月91天+额外天数)
var days = 91 + (extraDays.Get % 275);
var expireDate = DateTime.UtcNow.AddDays(-days);
var now = DateTime.UtcNow;
// 到期超过3个月积分应被清空
return IMembershipService.ShouldClearPoints(expireDate, now);
}
[Property(MaxTest = 100)]
public bool PointsNotCleared_WhenMembershipNotExpired(PositiveInt daysUntilExpire)
{
// 会员未到期未来1-365天
var days = (daysUntilExpire.Get % 365) + 1;
var expireDate = DateTime.UtcNow.AddDays(days);
var now = DateTime.UtcNow;
// 会员未到期,积分不应被清空
return !IMembershipService.ShouldClearPoints(expireDate, now);
}
// Feature: vending-machine-app, Property 17: 支付错误处理
// 对于任意支付错误无效凭证App 应拒绝操作且用户状态保持不变。
// **Validates: Requirements 8.4**
[Property(MaxTest = 100)]
public bool PaymentError_EmptyReceipt_ShouldReject(NonEmptyString platform)
{
// 空凭证应被拒绝
return !IMembershipService.ValidateReceipt("", platform.Get);
}
[Property(MaxTest = 100)]
public bool PaymentError_EmptyPlatform_ShouldReject(NonEmptyString receipt)
{
// 空平台应被拒绝
return !IMembershipService.ValidateReceipt(receipt.Get, "");
}
[Property(MaxTest = 100)]
public bool PaymentError_InvalidPlatform_ShouldReject(NonEmptyString receipt, NonEmptyString platform)
{
var p = platform.Get.ToLowerInvariant();
// 排除有效平台
if (p == "android" || p == "ios")
return true; // 跳过有效平台
return !IMembershipService.ValidateReceipt(receipt.Get, platform.Get);
}
[Property(MaxTest = 100)]
public bool PaymentValidation_ValidInput_ShouldAccept(NonEmptyString receipt, bool useAndroid)
{
var platform = useAndroid ? "android" : "ios";
var receiptStr = receipt.Get;
// 跳过纯空白字符串ValidateReceipt 正确地拒绝空白凭证)
if (string.IsNullOrWhiteSpace(receiptStr))
return true;
return IMembershipService.ValidateReceipt(receiptStr, platform);
}
}