FlowJoin - Improving unit tests, more DRY + integration test
This commit is contained in:
parent
86a5b62c29
commit
66b94edd61
|
|
@ -0,0 +1,228 @@
|
|||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Models;
|
||||
using Xunit.Abstractions;
|
||||
using static Elsa.Activities.IntegrationTests.Flow.FlowchartTestHelpers;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests.Branching;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for FlowJoin activity in complex flowchart scenarios.
|
||||
/// </summary>
|
||||
public class FlowJoinTests : IDisposable
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly CapturingTextWriter _output;
|
||||
private readonly bool _originalFlowMode;
|
||||
|
||||
public FlowJoinTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_output = new();
|
||||
_services = CreateServiceProvider(testOutputHelper, _output);
|
||||
_originalFlowMode = Flowchart.UseTokenFlow;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Flowchart.UseTokenFlow = _originalFlowMode;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, FlowJoinMode.WaitAny)]
|
||||
[InlineData(true, FlowJoinMode.WaitAll)]
|
||||
[InlineData(false, FlowJoinMode.WaitAny)]
|
||||
[InlineData(false, FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Handle_Complex_Flowchart_Scenarios(bool useTokenFlow, FlowJoinMode joinMode)
|
||||
{
|
||||
// Test with a more complex flowchart that has multiple activities
|
||||
// Arrange
|
||||
Flowchart.UseTokenFlow = useTokenFlow;
|
||||
|
||||
var startActivity = new WriteLine("Start");
|
||||
var flowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
var afterJoin = new WriteLine("AfterJoin");
|
||||
|
||||
var flowchart = new Flowchart
|
||||
{
|
||||
Start = startActivity,
|
||||
Activities = { startActivity, flowJoin, afterJoin },
|
||||
Connections =
|
||||
{
|
||||
new() { Source = new(startActivity, "Done"), Target = new(flowJoin) },
|
||||
new() { Source = new(flowJoin, "Done"), Target = new(afterJoin) }
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
await RunFlowchartAsync(_services, flowchart);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("Start", _output.Lines);
|
||||
|
||||
// Document expected behavior for different combinations
|
||||
if (useTokenFlow)
|
||||
{
|
||||
// In token flow mode, both WaitAny and WaitAll should act as no-ops
|
||||
// The flowchart should execute normally: Start -> FlowJoin -> AfterJoin
|
||||
Assert.Contains("AfterJoin", _output.Lines);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In counter flow mode:
|
||||
// - WaitAny should allow continuation after first inbound connection
|
||||
// - WaitAll should wait for all inbound connections (in this case just one)
|
||||
// Since there's only one inbound connection, both should behave similarly
|
||||
Assert.Contains("AfterJoin", _output.Lines);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, FlowJoinMode.WaitAny)]
|
||||
[InlineData(true, FlowJoinMode.WaitAll)]
|
||||
[InlineData(false, FlowJoinMode.WaitAny)]
|
||||
[InlineData(false, FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Handle_Fork_Join_Scenarios(bool useTokenFlow, FlowJoinMode joinMode)
|
||||
{
|
||||
// Test FlowJoin with a Fork-Join pattern
|
||||
// Arrange
|
||||
Flowchart.UseTokenFlow = useTokenFlow;
|
||||
|
||||
var start = new WriteLine("Start");
|
||||
var fork = new FlowFork { Branches = new(["Branch1", "Branch2"]) };
|
||||
var branch1 = new WriteLine("Branch1");
|
||||
var branch2 = new WriteLine("Branch2");
|
||||
var join = new FlowJoin { Mode = new(joinMode) };
|
||||
var afterJoin = new WriteLine("AfterJoin");
|
||||
|
||||
var flowchart = new Flowchart
|
||||
{
|
||||
Start = start,
|
||||
Activities = { start, fork, branch1, branch2, join, afterJoin },
|
||||
Connections =
|
||||
{
|
||||
new() { Source = new(start, "Done"), Target = new(fork) },
|
||||
new() { Source = new(fork, "Branch1"), Target = new(branch1) },
|
||||
new() { Source = new(fork, "Branch2"), Target = new(branch2) },
|
||||
new() { Source = new(branch1, "Done"), Target = new(join) },
|
||||
new() { Source = new(branch2, "Done"), Target = new(join) },
|
||||
new() { Source = new(join, "Done"), Target = new(afterJoin) }
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
await RunFlowchartAsync(_services, flowchart);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("Start", _output.Lines);
|
||||
|
||||
if (useTokenFlow)
|
||||
{
|
||||
// In token flow mode, FlowJoin acts as a no-op
|
||||
// Both branches should execute and continue to AfterJoin
|
||||
Assert.Contains("Branch1", _output.Lines);
|
||||
Assert.Contains("Branch2", _output.Lines);
|
||||
Assert.Contains("AfterJoin", _output.Lines);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In counter flow mode, behavior depends on join mode
|
||||
if (joinMode == FlowJoinMode.WaitAny)
|
||||
{
|
||||
// WaitAny allows continuation after first branch completes
|
||||
Assert.Contains("AfterJoin", _output.Lines);
|
||||
// At least one branch should execute
|
||||
Assert.True(_output.Lines.Contains("Branch1") || _output.Lines.Contains("Branch2"));
|
||||
}
|
||||
else // WaitAll
|
||||
{
|
||||
// WaitAll waits for both branches to complete
|
||||
Assert.Contains("Branch1", _output.Lines);
|
||||
Assert.Contains("Branch2", _output.Lines);
|
||||
Assert.Contains("AfterJoin", _output.Lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, FlowJoinMode.WaitAny)]
|
||||
[InlineData(true, FlowJoinMode.WaitAll)]
|
||||
[InlineData(false, FlowJoinMode.WaitAny)]
|
||||
[InlineData(false, FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Handle_Multiple_Join_Scenarios(bool useTokenFlow, FlowJoinMode joinMode)
|
||||
{
|
||||
// Test multiple FlowJoin activities in a complex flowchart
|
||||
// Arrange
|
||||
Flowchart.UseTokenFlow = useTokenFlow;
|
||||
|
||||
var start = new WriteLine("Start");
|
||||
var fork1 = new FlowFork { Branches = new(["A", "B"]) };
|
||||
var activityA = new WriteLine("A");
|
||||
var activityB = new WriteLine("B");
|
||||
var join1 = new FlowJoin { Mode = new(joinMode) };
|
||||
var middle = new WriteLine("Middle");
|
||||
var fork2 = new FlowFork { Branches = new(["C", "D"]) };
|
||||
var activityC = new WriteLine("C");
|
||||
var activityD = new WriteLine("D");
|
||||
var join2 = new FlowJoin { Mode = new(joinMode) };
|
||||
var end = new WriteLine("End");
|
||||
|
||||
var flowchart = new Flowchart
|
||||
{
|
||||
Start = start,
|
||||
Activities = { start, fork1, activityA, activityB, join1, middle, fork2, activityC, activityD, join2, end },
|
||||
Connections =
|
||||
{
|
||||
new() { Source = new(start, "Done"), Target = new(fork1) },
|
||||
new() { Source = new(fork1, "A"), Target = new(activityA) },
|
||||
new() { Source = new(fork1, "B"), Target = new(activityB) },
|
||||
new() { Source = new(activityA, "Done"), Target = new(join1) },
|
||||
new() { Source = new(activityB, "Done"), Target = new(join1) },
|
||||
new() { Source = new(join1, "Done"), Target = new(middle) },
|
||||
new() { Source = new(middle, "Done"), Target = new(fork2) },
|
||||
new() { Source = new(fork2, "C"), Target = new(activityC) },
|
||||
new() { Source = new(fork2, "D"), Target = new(activityD) },
|
||||
new() { Source = new(activityC, "Done"), Target = new(join2) },
|
||||
new() { Source = new(activityD, "Done"), Target = new(join2) },
|
||||
new() { Source = new(join2, "Done"), Target = new(end) }
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
await RunFlowchartAsync(_services, flowchart);
|
||||
|
||||
// Assert
|
||||
Assert.Contains("Start", _output.Lines);
|
||||
Assert.Contains("Middle", _output.Lines);
|
||||
Assert.Contains("End", _output.Lines);
|
||||
|
||||
// Verify expected execution patterns based on flow mode and join mode
|
||||
if (useTokenFlow)
|
||||
{
|
||||
// In token flow mode, all activities should execute
|
||||
Assert.Contains("A", _output.Lines);
|
||||
Assert.Contains("B", _output.Lines);
|
||||
Assert.Contains("C", _output.Lines);
|
||||
Assert.Contains("D", _output.Lines);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In counter flow mode, execution depends on join mode
|
||||
if (joinMode == FlowJoinMode.WaitAll)
|
||||
{
|
||||
// WaitAll ensures all parallel branches complete
|
||||
Assert.Contains("A", _output.Lines);
|
||||
Assert.Contains("B", _output.Lines);
|
||||
Assert.Contains("C", _output.Lines);
|
||||
Assert.Contains("D", _output.Lines);
|
||||
}
|
||||
else // WaitAny
|
||||
{
|
||||
// WaitAny allows continuation after first branch in each fork
|
||||
// At least one from each pair should execute
|
||||
Assert.True(_output.Lines.Contains("A") || _output.Lines.Contains("B"));
|
||||
Assert.True(_output.Lines.Contains("C") || _output.Lines.Contains("D"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,86 +10,6 @@ namespace Elsa.Activities.UnitTests.Branching;
|
|||
/// </summary>
|
||||
public class FlowJoinTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(FlowJoinMode.WaitAny)]
|
||||
[InlineData(FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Complete_Activity_In_Token_Flow_Mode(FlowJoinMode joinMode)
|
||||
{
|
||||
// Arrange
|
||||
var originalValue = Flowchart.UseTokenFlow;
|
||||
Flowchart.UseTokenFlow = true;
|
||||
|
||||
try
|
||||
{
|
||||
var flowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
|
||||
// Act
|
||||
var context = await ExecuteAsync(flowJoin);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsCompleted);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Flowchart.UseTokenFlow = originalValue;
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(FlowJoinMode.WaitAny)]
|
||||
[InlineData(FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Complete_Activity_In_Counter_Flow_Mode(FlowJoinMode joinMode)
|
||||
{
|
||||
// Arrange
|
||||
var originalValue = Flowchart.UseTokenFlow;
|
||||
Flowchart.UseTokenFlow = false;
|
||||
|
||||
try
|
||||
{
|
||||
var flowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
|
||||
// Act - Execute without parent context (simple case)
|
||||
var context = await ExecuteAsync(flowJoin);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsCompleted);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Flowchart.UseTokenFlow = originalValue;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Execute_In_Flowchart_Context()
|
||||
{
|
||||
// Arrange
|
||||
var originalValue = Flowchart.UseTokenFlow;
|
||||
Flowchart.UseTokenFlow = false;
|
||||
|
||||
try
|
||||
{
|
||||
var flowJoin = new FlowJoin { Mode = new(FlowJoinMode.WaitAny) };
|
||||
var flowchart = new Flowchart
|
||||
{
|
||||
Start = flowJoin,
|
||||
Activities = { flowJoin }
|
||||
};
|
||||
|
||||
// Act
|
||||
var context = await ExecuteFlowchartAsync(flowchart);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context);
|
||||
Assert.True(context.HasScheduledActivity(flowJoin));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Flowchart.UseTokenFlow = originalValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Comprehensive tests covering all flow mode combinations
|
||||
[Theory]
|
||||
[InlineData(true, FlowJoinMode.WaitAny)]
|
||||
[InlineData(true, FlowJoinMode.WaitAll)]
|
||||
|
|
@ -97,38 +17,11 @@ public class FlowJoinTests
|
|||
[InlineData(false, FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Complete_In_All_Flow_Mode_Combinations(bool useTokenFlow, FlowJoinMode joinMode)
|
||||
{
|
||||
// Arrange
|
||||
var originalValue = Flowchart.UseTokenFlow;
|
||||
Flowchart.UseTokenFlow = useTokenFlow;
|
||||
// Arrange & Act
|
||||
var context = await ExecuteWithFlowModeAsync(useTokenFlow, joinMode);
|
||||
|
||||
try
|
||||
{
|
||||
var flowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
|
||||
// Act
|
||||
var context = await ExecuteAsync(flowJoin);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsCompleted);
|
||||
|
||||
// Document expected behavior based on flow mode
|
||||
if (useTokenFlow)
|
||||
{
|
||||
// In token flow mode, FlowJoin acts as a no-op and always completes
|
||||
// regardless of join mode (WaitAny or WaitAll)
|
||||
Assert.True(context.IsCompleted, "Token flow mode should always complete");
|
||||
}
|
||||
else
|
||||
{
|
||||
// In counter flow mode, behavior depends on join mode and flowchart state
|
||||
// Without parent context, both WaitAny and WaitAll should complete
|
||||
Assert.True(context.IsCompleted, "Counter flow mode should complete when no parent context");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Flowchart.UseTokenFlow = originalValue;
|
||||
}
|
||||
// Assert
|
||||
Assert.True(context.IsCompleted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -138,139 +31,85 @@ public class FlowJoinTests
|
|||
[InlineData(false, FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Execute_In_Flowchart_Context_For_All_Combinations(bool useTokenFlow, FlowJoinMode joinMode)
|
||||
{
|
||||
// Arrange
|
||||
var originalValue = Flowchart.UseTokenFlow;
|
||||
Flowchart.UseTokenFlow = useTokenFlow;
|
||||
// Arrange & Act
|
||||
var (context, flowJoin) = await ExecuteInFlowchartWithFlowModeAsync(useTokenFlow, joinMode);
|
||||
|
||||
try
|
||||
{
|
||||
var flowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
var flowchart = new Flowchart
|
||||
{
|
||||
Start = flowJoin,
|
||||
Activities = { flowJoin }
|
||||
};
|
||||
// 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";
|
||||
|
||||
// Act
|
||||
var context = await ExecuteFlowchartAsync(flowchart);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context);
|
||||
|
||||
if (useTokenFlow)
|
||||
{
|
||||
// In token flow mode, the join should be scheduled regardless of join mode
|
||||
Assert.True(context.HasScheduledActivity(flowJoin),
|
||||
$"Token flow mode with {joinMode} should schedule the join activity");
|
||||
}
|
||||
else
|
||||
{
|
||||
// In counter flow mode, scheduling depends on join mode and flowchart state
|
||||
// Since this is a simple flowchart with just the join as start activity,
|
||||
// it should be scheduled
|
||||
Assert.True(context.HasScheduledActivity(flowJoin),
|
||||
$"Counter flow mode with {joinMode} should schedule the join activity as start");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Flowchart.UseTokenFlow = originalValue;
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, FlowJoinMode.WaitAny)]
|
||||
[InlineData(true, FlowJoinMode.WaitAll)]
|
||||
[InlineData(false, FlowJoinMode.WaitAny)]
|
||||
[InlineData(false, FlowJoinMode.WaitAll)]
|
||||
public async Task Should_Handle_Complex_Flowchart_Scenarios(bool useTokenFlow, FlowJoinMode joinMode)
|
||||
{
|
||||
// Test with a more complex flowchart that has multiple activities
|
||||
// Arrange
|
||||
var originalValue = Flowchart.UseTokenFlow;
|
||||
Flowchart.UseTokenFlow = useTokenFlow;
|
||||
|
||||
try
|
||||
{
|
||||
var startActivity = new WriteLine("Start");
|
||||
var flowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
var afterJoin = new WriteLine("AfterJoin");
|
||||
|
||||
var flowchart = new Flowchart
|
||||
{
|
||||
Start = startActivity,
|
||||
Activities = { startActivity, flowJoin, afterJoin },
|
||||
Connections =
|
||||
{
|
||||
new() { Source = new(startActivity, "Done"), Target = new(flowJoin) },
|
||||
new() { Source = new(flowJoin, "Done"), Target = new(afterJoin) }
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var context = await ExecuteFlowchartAsync(flowchart);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context);
|
||||
|
||||
// The start activity should always be scheduled first
|
||||
Assert.True(context.HasScheduledActivity(startActivity),
|
||||
"Start activity should be scheduled");
|
||||
|
||||
// Document expected behavior for different combinations
|
||||
if (useTokenFlow)
|
||||
{
|
||||
// In token flow mode, both WaitAny and WaitAll act as no-ops
|
||||
// The exact scheduling depends on the flowchart execution engine
|
||||
}
|
||||
else
|
||||
{
|
||||
// In counter flow mode:
|
||||
// - WaitAny should allow continuation after first inbound connection
|
||||
// - WaitAll should wait for all inbound connections (in this case just one)
|
||||
// Since there's only one inbound connection, both should behave similarly
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Flowchart.UseTokenFlow = originalValue;
|
||||
}
|
||||
Assert.True(context.HasScheduledActivity(flowJoin), expectedMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Demonstrate_Token_vs_Counter_Flow_Differences()
|
||||
{
|
||||
// This test demonstrates the key differences between token and counter flow modes
|
||||
// Arrange
|
||||
var joinMode = FlowJoinMode.WaitAll; // Use WaitAll to highlight differences
|
||||
|
||||
// Test Token Flow Mode
|
||||
// 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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a FlowJoin activity with the specified mode.
|
||||
/// </summary>
|
||||
private static FlowJoin CreateFlowJoin(FlowJoinMode joinMode) => new() { Mode = new(joinMode) };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a simple flowchart with the FlowJoin as the start activity.
|
||||
/// </summary>
|
||||
private static Flowchart CreateSimpleFlowchart(FlowJoin flowJoin) => new()
|
||||
{
|
||||
Start = flowJoin,
|
||||
Activities = { flowJoin }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Executes a FlowJoin activity with the specified flow mode, handling the UseTokenFlow setup and teardown.
|
||||
/// </summary>
|
||||
private static async Task<ActivityExecutionContext> ExecuteWithFlowModeAsync(bool useTokenFlow, FlowJoinMode joinMode)
|
||||
{
|
||||
return await WithFlowModeAsync(useTokenFlow, async () =>
|
||||
{
|
||||
var flowJoin = CreateFlowJoin(joinMode);
|
||||
return await ExecuteAsync(flowJoin);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a FlowJoin activity within a flowchart context with the specified flow mode.
|
||||
/// </summary>
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes an action with the specified flow mode, ensuring proper setup and teardown of UseTokenFlow.
|
||||
/// </summary>
|
||||
private static async Task<T> WithFlowModeAsync<T>(bool useTokenFlow, Func<Task<T>> action)
|
||||
{
|
||||
var originalValue = Flowchart.UseTokenFlow;
|
||||
Flowchart.UseTokenFlow = true;
|
||||
|
||||
Flowchart.UseTokenFlow = useTokenFlow;
|
||||
|
||||
try
|
||||
{
|
||||
var tokenFlowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
var tokenContext = await ExecuteAsync(tokenFlowJoin);
|
||||
|
||||
// Token flow: FlowJoin acts as no-op, always completes
|
||||
Assert.True(tokenContext.IsCompleted, "Token flow should always complete");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Flowchart.UseTokenFlow = originalValue;
|
||||
}
|
||||
|
||||
// Test Counter Flow Mode
|
||||
Flowchart.UseTokenFlow = false;
|
||||
|
||||
try
|
||||
{
|
||||
var counterFlowJoin = new FlowJoin { Mode = new(joinMode) };
|
||||
var counterContext = await ExecuteAsync(counterFlowJoin);
|
||||
|
||||
// Counter flow: Behavior depends on CanWaitAllProceed for WaitAll mode
|
||||
// Without parent context, it should complete
|
||||
Assert.True(counterContext.IsCompleted, "Counter flow should complete without parent context");
|
||||
return await action();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -278,11 +117,17 @@ public class FlowJoinTests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes an activity using the ActivityTestFixture.
|
||||
/// </summary>
|
||||
private static async Task<ActivityExecutionContext> ExecuteAsync(IActivity activity)
|
||||
{
|
||||
return await new ActivityTestFixture(activity).ExecuteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a flowchart using the ActivityTestFixture.
|
||||
/// </summary>
|
||||
private static async Task<ActivityExecutionContext> ExecuteFlowchartAsync(Flowchart flowchart)
|
||||
{
|
||||
return await new ActivityTestFixture(flowchart).ExecuteAsync();
|
||||
|
|
|
|||
Loading…
Reference in a new issue