using FluentAssertions; using Workflow.Domain.Enums; using Workflow.Domain.Exceptions; using Workflow.Domain.StateMachine; using Xunit; namespace Workflow.Tests.StateMachine; /// /// InstanceStatusMachine TDD 红灯阶段测试 /// 测试工作流实例状态机的所有合法转换与非法转换 /// /// 实例状态: Running, Suspended, Completed, Terminated /// 操作: Complete, Suspend, Resume, Terminate, Withdraw /// public class InstanceStatusMachineTests { private readonly InstanceStateMachine _machine = new(); #region Running → Completed [Fact] public void Transition_Running_To_Completed_When_No_Active_Tokens() { // Arrange var currentState = InstanceStatus.Running; var operation = InstanceOperation.Complete; var context = new InstanceTransitionContext { HasActiveTokens = false, }; // Act var newState = _machine.Transition(currentState, operation, context); // Assert newState.Should().Be(InstanceStatus.Completed); } [Fact] public void Transition_Running_Complete_With_Active_Tokens_Should_Throw() { // Arrange var currentState = InstanceStatus.Running; var operation = InstanceOperation.Complete; var context = new InstanceTransitionContext { HasActiveTokens = true, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw() .WithMessage("*active token*"); } #endregion #region Running → Suspended [Fact] public void Transition_Running_To_Suspended_By_Admin() { // Arrange var currentState = InstanceStatus.Running; var operation = InstanceOperation.Suspend; var context = new InstanceTransitionContext { IsAdmin = true, IsInitiator = false, }; // Act var newState = _machine.Transition(currentState, operation, context); // Assert newState.Should().Be(InstanceStatus.Suspended); } [Fact] public void Transition_Running_To_Suspended_By_Initiator() { // Arrange var currentState = InstanceStatus.Running; var operation = InstanceOperation.Suspend; var context = new InstanceTransitionContext { IsAdmin = false, IsInitiator = true, }; // Act var newState = _machine.Transition(currentState, operation, context); // Assert newState.Should().Be(InstanceStatus.Suspended); } [Fact] public void Transition_Running_Suspend_By_Unauthorized_User_Should_Throw() { // Arrange - 既不是 Admin 也不是 Initiator var currentState = InstanceStatus.Running; var operation = InstanceOperation.Suspend; var context = new InstanceTransitionContext { IsAdmin = false, IsInitiator = false, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw() .WithMessage("*admin*initiator*"); } #endregion #region Suspended → Running [Fact] public void Transition_Suspended_To_Running_By_Admin() { // Arrange var currentState = InstanceStatus.Suspended; var operation = InstanceOperation.Resume; var context = new InstanceTransitionContext { IsAdmin = true, IsInitiator = false, }; // Act var newState = _machine.Transition(currentState, operation, context); // Assert newState.Should().Be(InstanceStatus.Running); } [Fact] public void Transition_Suspended_To_Running_By_Initiator() { // Arrange var currentState = InstanceStatus.Suspended; var operation = InstanceOperation.Resume; var context = new InstanceTransitionContext { IsAdmin = false, IsInitiator = true, }; // Act var newState = _machine.Transition(currentState, operation, context); // Assert newState.Should().Be(InstanceStatus.Running); } [Fact] public void Transition_Suspended_Resume_By_Unauthorized_User_Should_Throw() { // Arrange var currentState = InstanceStatus.Suspended; var operation = InstanceOperation.Resume; var context = new InstanceTransitionContext { IsAdmin = false, IsInitiator = false, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw() .WithMessage("*admin*initiator*"); } #endregion #region Running → Terminated [Fact] public void Transition_Running_To_Terminated_By_Admin() { // Arrange var currentState = InstanceStatus.Running; var operation = InstanceOperation.Terminate; var context = new InstanceTransitionContext { IsAdmin = true, }; // Act var newState = _machine.Transition(currentState, operation, context); // Assert newState.Should().Be(InstanceStatus.Terminated); } [Fact] public void Transition_Running_Terminate_By_Non_Admin_Should_Throw() { // Arrange - Terminate 操作要求 Admin 权限 var currentState = InstanceStatus.Running; var operation = InstanceOperation.Terminate; var context = new InstanceTransitionContext { IsAdmin = false, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw() .WithMessage("*admin*"); } #endregion #region Running → Terminated (Withdraw) [Fact] public void Transition_Running_Withdraw_By_Initiator_With_No_Processed_Nodes() { // Arrange - 发起人撤回,且没有任何节点被处理过 var currentState = InstanceStatus.Running; var operation = InstanceOperation.Withdraw; var context = new InstanceTransitionContext { IsInitiator = true, HasProcessedNodes = false, }; // Act var newState = _machine.Transition(currentState, operation, context); // Assert newState.Should().Be(InstanceStatus.Terminated); } [Fact] public void Transition_Running_Withdraw_By_Non_Initiator_Should_Throw() { // Arrange - 非发起人尝试撤回 var currentState = InstanceStatus.Running; var operation = InstanceOperation.Withdraw; var context = new InstanceTransitionContext { IsInitiator = false, HasProcessedNodes = false, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw() .WithMessage("*initiator*"); } [Fact] public void Transition_Running_Withdraw_With_Processed_Nodes_Should_Throw() { // Arrange - 发起人撤回,但已有节点被处理过 var currentState = InstanceStatus.Running; var operation = InstanceOperation.Withdraw; var context = new InstanceTransitionContext { IsInitiator = true, HasProcessedNodes = true, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw() .WithMessage("*processed*"); } #endregion #region Illegal Transitions [Fact] public void Transition_Completed_To_Running_Should_Throw() { // Arrange - 已完成的实例不能回到运行中 var currentState = InstanceStatus.Completed; var operation = InstanceOperation.Resume; var context = new InstanceTransitionContext { IsAdmin = true, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw(); } [Fact] public void Transition_Terminated_To_Suspended_Should_Throw() { // Arrange - 已终止的实例不能挂起 var currentState = InstanceStatus.Terminated; var operation = InstanceOperation.Suspend; var context = new InstanceTransitionContext { IsAdmin = true, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw(); } [Fact] public void Transition_Terminated_To_Running_Should_Throw() { // Arrange - 已终止的实例不能恢复运行 var currentState = InstanceStatus.Terminated; var operation = InstanceOperation.Resume; var context = new InstanceTransitionContext { IsAdmin = true, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw(); } [Fact] public void Transition_Completed_To_Terminated_Should_Throw() { // Arrange - 已完成的实例不能终止 var currentState = InstanceStatus.Completed; var operation = InstanceOperation.Terminate; var context = new InstanceTransitionContext { IsAdmin = true, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw(); } [Fact] public void Transition_Suspended_To_Completed_Should_Throw() { // Arrange - 挂起状态的实例不能直接完成 var currentState = InstanceStatus.Suspended; var operation = InstanceOperation.Complete; var context = new InstanceTransitionContext { HasActiveTokens = false, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw(); } [Fact] public void Transition_Suspended_To_Terminated_Should_Throw() { // Arrange - 挂起状态的实例不能直接终止,需先恢复 var currentState = InstanceStatus.Suspended; var operation = InstanceOperation.Terminate; var context = new InstanceTransitionContext { IsAdmin = true, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw(); } [Fact] public void Transition_Suspended_Withdraw_Should_Throw() { // Arrange - 挂起状态不允许撤回操作 var currentState = InstanceStatus.Suspended; var operation = InstanceOperation.Withdraw; var context = new InstanceTransitionContext { IsInitiator = true, HasProcessedNodes = false, }; // Act var act = () => _machine.Transition(currentState, operation, context); // Assert act.Should().Throw(); } #endregion }