using FsCheck;
using FsCheck.Xunit;
using Xunit;
using XiangYi.Application.Services;
using XiangYi.Core.Enums;
namespace XiangYi.Application.Tests.Services;
///
/// AdminProfileAuditService属性测试
///
public class AdminProfileAuditServicePropertyTests
{
///
/// **Feature: backend-api, Property 24: 资料审核状态流转**
/// **Validates: Requirements 14.1, 14.3, 14.4, 14.5**
///
/// *For any* 用户资料, 状态应从"待审核"流转到"已通过"或"已拒绝",
/// 且只有"已通过"状态的资料才能在列表展示
///
/// 此测试验证:从待审核状态可以流转到已通过或已拒绝
///
[Property(MaxTest = 100)]
public Property StatusTransition_FromPending_ShouldAllowApproveOrReject()
{
// Generate target status (Approved or Rejected)
var targetStatusArb = Gen.Elements(
(int)AuditStatus.Approved,
(int)AuditStatus.Rejected);
return Prop.ForAll(
targetStatusArb.ToArbitrary(),
targetStatus =>
{
// Act
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
(int)AuditStatus.Pending,
targetStatus);
// Assert - From Pending, should allow transition to Approved or Rejected
return isValid;
});
}
///
/// 状态流转 - 从待审核不能流转到待审核
///
[Fact]
public void StatusTransition_FromPending_ToPending_ShouldBeInvalid()
{
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
(int)AuditStatus.Pending,
(int)AuditStatus.Pending);
Assert.False(isValid);
}
///
/// 状态流转 - 从已通过不能流转到任何状态
///
[Property(MaxTest = 100)]
public Property StatusTransition_FromApproved_ShouldNotAllowAnyTransition()
{
// Generate any target status
var targetStatusArb = Gen.Elements(
(int)AuditStatus.Pending,
(int)AuditStatus.Approved,
(int)AuditStatus.Rejected);
return Prop.ForAll(
targetStatusArb.ToArbitrary(),
targetStatus =>
{
// Act
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
(int)AuditStatus.Approved,
targetStatus);
// Assert - From Approved, should not allow any transition
return !isValid;
});
}
///
/// 状态流转 - 从已拒绝可以流转到已通过(重新审核通过)
///
[Fact]
public void StatusTransition_FromRejected_ToApproved_ShouldBeValid()
{
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
(int)AuditStatus.Rejected,
(int)AuditStatus.Approved);
Assert.True(isValid);
}
///
/// 状态流转 - 从已拒绝可以流转到已拒绝(更新拒绝原因)
///
[Fact]
public void StatusTransition_FromRejected_ToRejected_ShouldBeValid()
{
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
(int)AuditStatus.Rejected,
(int)AuditStatus.Rejected);
Assert.True(isValid);
}
///
/// 状态流转 - 从已拒绝不能流转到待审核
///
[Fact]
public void StatusTransition_FromRejected_ToPending_ShouldBeInvalid()
{
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
(int)AuditStatus.Rejected,
(int)AuditStatus.Pending);
Assert.False(isValid);
}
///
/// 状态流转 - 任何状态都不能手动流转到待审核
///
[Property(MaxTest = 100)]
public Property StatusTransition_ToPending_ShouldAlwaysBeInvalid()
{
// Generate any current status
var currentStatusArb = Gen.Elements(
(int)AuditStatus.Pending,
(int)AuditStatus.Approved,
(int)AuditStatus.Rejected);
return Prop.ForAll(
currentStatusArb.ToArbitrary(),
currentStatus =>
{
// Act
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
currentStatus,
(int)AuditStatus.Pending);
// Assert - Should never allow transition to Pending
return !isValid;
});
}
///
/// 列表展示 - 只有已通过状态的资料才能在列表展示
///
[Property(MaxTest = 100)]
public Property CanDisplayInList_OnlyApprovedShouldBeDisplayable()
{
// Generate any audit status
var auditStatusArb = Gen.Elements(
(int)AuditStatus.Pending,
(int)AuditStatus.Approved,
(int)AuditStatus.Rejected);
return Prop.ForAll(
auditStatusArb.ToArbitrary(),
auditStatus =>
{
// Act
var canDisplay = AdminProfileAuditService.CanDisplayInList(auditStatus);
// Assert - Only Approved status should be displayable
var expectedResult = auditStatus == (int)AuditStatus.Approved;
return canDisplay == expectedResult;
});
}
///
/// 列表展示 - 待审核状态不能展示
///
[Fact]
public void CanDisplayInList_Pending_ShouldNotBeDisplayable()
{
var canDisplay = AdminProfileAuditService.CanDisplayInList((int)AuditStatus.Pending);
Assert.False(canDisplay);
}
///
/// 列表展示 - 已拒绝状态不能展示
///
[Fact]
public void CanDisplayInList_Rejected_ShouldNotBeDisplayable()
{
var canDisplay = AdminProfileAuditService.CanDisplayInList((int)AuditStatus.Rejected);
Assert.False(canDisplay);
}
///
/// 列表展示 - 已通过状态可以展示
///
[Fact]
public void CanDisplayInList_Approved_ShouldBeDisplayable()
{
var canDisplay = AdminProfileAuditService.CanDisplayInList((int)AuditStatus.Approved);
Assert.True(canDisplay);
}
///
/// 状态流转完整性 - 从待审核到已通过或已拒绝的流转应该是完整的
///
[Property(MaxTest = 100)]
public Property StatusTransition_CompleteWorkflow_ShouldBeValid()
{
// Generate a sequence of valid transitions
var workflowArb = Gen.Elements(
new[] { (int)AuditStatus.Pending, (int)AuditStatus.Approved },
new[] { (int)AuditStatus.Pending, (int)AuditStatus.Rejected },
new[] { (int)AuditStatus.Pending, (int)AuditStatus.Rejected, (int)AuditStatus.Approved },
new[] { (int)AuditStatus.Pending, (int)AuditStatus.Rejected, (int)AuditStatus.Rejected });
return Prop.ForAll(
workflowArb.ToArbitrary(),
workflow =>
{
// Verify each transition in the workflow is valid
for (int i = 0; i < workflow.Length - 1; i++)
{
var isValid = AdminProfileAuditService.IsValidStatusTransitionStatic(
workflow[i],
workflow[i + 1]);
if (!isValid) return false;
}
return true;
});
}
///
/// 状态流转 - 无效状态值应该被拒绝
///
[Property(MaxTest = 100)]
public Property StatusTransition_InvalidStatusValues_ShouldBeRejected()
{
// Generate invalid status values (outside 0, 1, 2)
var invalidStatusArb = Gen.Choose(-10, 10)
.Where(s => s != 0 && s != 1 && s != 2);
var validStatusArb = Gen.Elements(0, 1, 2);
return Prop.ForAll(
invalidStatusArb.ToArbitrary(),
validStatusArb.ToArbitrary(),
(invalidStatus, validStatus) =>
{
// Act - Invalid current status
var isValid1 = AdminProfileAuditService.IsValidStatusTransitionStatic(
invalidStatus,
validStatus);
// Act - Invalid target status
var isValid2 = AdminProfileAuditService.IsValidStatusTransitionStatic(
validStatus,
invalidStatus);
// Assert - Both should be invalid
return !isValid1 && !isValid2;
});
}
}