work-flow/tests/Workflow.Tests/StateMachine/WorkflowInstanceStateMachineTests.cs
2026-05-17 22:46:19 +08:00

420 lines
12 KiB
C#

using FluentAssertions;
using Workflow.Domain.Enums;
using Workflow.Domain.Exceptions;
using Workflow.Domain.StateMachine;
using Xunit;
namespace Workflow.Tests.StateMachine;
/// <summary>
/// InstanceStatusMachine TDD 红灯阶段测试
/// 测试工作流实例状态机的所有合法转换与非法转换
///
/// 实例状态: Running, Suspended, Completed, Terminated
/// 操作: Complete, Suspend, Resume, Terminate, Withdraw
/// </summary>
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<InvalidStateTransitionException>()
.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<InvalidStateTransitionException>()
.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<InvalidStateTransitionException>()
.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<InvalidStateTransitionException>()
.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<InvalidStateTransitionException>()
.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<InvalidStateTransitionException>()
.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<InvalidStateTransitionException>();
}
[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<InvalidStateTransitionException>();
}
[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<InvalidStateTransitionException>();
}
[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<InvalidStateTransitionException>();
}
[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<InvalidStateTransitionException>();
}
[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<InvalidStateTransitionException>();
}
[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<InvalidStateTransitionException>();
}
#endregion
}