using Elsa.Testing.Shared; using Elsa.Workflows; using Elsa.Workflows.Activities.Flowchart.Activities; using Elsa.Workflows.Activities.Flowchart.Models; namespace Elsa.Activities.UnitTests.Branching; /// /// Unit tests for FlowJoin activity covering both token flow and counter flow modes. /// public class FlowJoinTests { [Theory] [InlineData(true, FlowJoinMode.WaitAny)] [InlineData(true, FlowJoinMode.WaitAll)] [InlineData(false, FlowJoinMode.WaitAny)] [InlineData(false, FlowJoinMode.WaitAll)] public async Task Should_Complete_In_All_Flow_Mode_Combinations(bool useTokenFlow, FlowJoinMode joinMode) { // Arrange & Act var context = await ExecuteWithFlowModeAsync(useTokenFlow, joinMode); // Assert Assert.True(context.IsCompleted); } [Theory] [InlineData(true, FlowJoinMode.WaitAny)] [InlineData(true, FlowJoinMode.WaitAll)] [InlineData(false, FlowJoinMode.WaitAny)] [InlineData(false, FlowJoinMode.WaitAll)] public async Task Should_Execute_In_Flowchart_Context_For_All_Combinations(bool useTokenFlow, FlowJoinMode joinMode) { // Arrange & Act var (context, flowJoin) = await ExecuteInFlowchartWithFlowModeAsync(useTokenFlow, joinMode); // Assert Assert.NotNull(context); var expectedMessage = useTokenFlow ? $"Token flow mode with {joinMode} should schedule the join activity" : $"Counter flow mode with {joinMode} should schedule the join activity as start"; Assert.True(context.HasScheduledActivity(flowJoin), expectedMessage); } [Fact] public async Task Should_Demonstrate_Token_vs_Counter_Flow_Differences() { // Arrange var joinMode = FlowJoinMode.WaitAll; // Use WaitAll to highlight differences // Act var tokenContext = await ExecuteWithFlowModeAsync(true, joinMode); var counterContext = await ExecuteWithFlowModeAsync(false, joinMode); // Assert Assert.True(tokenContext.IsCompleted, "Token flow should always complete"); Assert.True(counterContext.IsCompleted, "Counter flow should complete without parent context"); } /// /// Creates a FlowJoin activity with the specified mode. /// private static FlowJoin CreateFlowJoin(FlowJoinMode joinMode) => new() { Mode = new(joinMode) }; /// /// Creates a simple flowchart with the FlowJoin as the start activity. /// private static Flowchart CreateSimpleFlowchart(FlowJoin flowJoin) => new() { Start = flowJoin, Activities = { flowJoin } }; /// /// Executes a FlowJoin activity with the specified flow mode, handling the UseTokenFlow setup and teardown. /// private static async Task ExecuteWithFlowModeAsync(bool useTokenFlow, FlowJoinMode joinMode) { return await WithFlowModeAsync(useTokenFlow, async () => { var flowJoin = CreateFlowJoin(joinMode); return await ExecuteAsync(flowJoin); }); } /// /// Executes a FlowJoin activity within a flowchart context with the specified flow mode. /// private static async Task<(ActivityExecutionContext context, FlowJoin flowJoin)> ExecuteInFlowchartWithFlowModeAsync(bool useTokenFlow, FlowJoinMode joinMode) { return await WithFlowModeAsync(useTokenFlow, async () => { var flowJoin = CreateFlowJoin(joinMode); var flowchart = CreateSimpleFlowchart(flowJoin); var context = await ExecuteFlowchartAsync(flowchart); return (context, flowJoin); }); } /// /// Executes an action with the specified flow mode, ensuring proper setup and teardown of UseTokenFlow. /// private static async Task WithFlowModeAsync(bool useTokenFlow, Func> action) { var originalValue = Flowchart.UseTokenFlow; Flowchart.UseTokenFlow = useTokenFlow; try { return await action(); } finally { Flowchart.UseTokenFlow = originalValue; } } /// /// Executes an activity using the ActivityTestFixture. /// private static async Task ExecuteAsync(IActivity activity) { return await new ActivityTestFixture(activity).ExecuteAsync(); } /// /// Executes a flowchart using the ActivityTestFixture. /// private static async Task ExecuteFlowchartAsync(Flowchart flowchart) { return await new ActivityTestFixture(flowchart).ExecuteAsync(); } }