elsa-core/test/integration/Elsa.Activities.IntegrationTests/ParallelTests.cs

183 lines
6.7 KiB
C#

using Elsa.Testing.Shared;
using Elsa.Workflows;
using Elsa.Workflows.Activities;
using Xunit.Abstractions;
using Parallel = Elsa.Workflows.Activities.Parallel;
namespace Elsa.Activities.IntegrationTests;
public class ParallelTests(ITestOutputHelper testOutputHelper)
{
private readonly WorkflowTestFixture _fixture = new(testOutputHelper);
[Fact(DisplayName = "Parallel executes all child activities and completes")]
public async Task Parallel_ExecutesAllChildren_AndCompletes()
{
// Arrange
var parallel = new Parallel(
new WriteLine("Activity 1"),
new WriteLine("Activity 2"),
new WriteLine("Activity 3")
);
// Act
var result = await _fixture.RunActivityAsync(parallel);
// Assert
var journal = result.Journal;
var parallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity is Parallel);
Assert.NotNull(parallelContext);
Assert.Equal(ActivityStatus.Completed, parallelContext.Status);
Assert.Equal(3, _fixture.CapturingTextWriter.Lines.Count);
Assert.Contains("Activity 1", _fixture.CapturingTextWriter.Lines);
Assert.Contains("Activity 2", _fixture.CapturingTextWriter.Lines);
Assert.Contains("Activity 3", _fixture.CapturingTextWriter.Lines);
}
[Fact(DisplayName = "Parallel completes when empty")]
public async Task Parallel_Completes_WhenEmpty()
{
// Arrange
var parallel = new Parallel();
// Act
var result = await _fixture.RunActivityAsync(parallel);
// Assert
var journal = result.Journal;
var parallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity is Parallel);
Assert.NotNull(parallelContext);
Assert.Equal(ActivityStatus.Completed, parallelContext.Status);
Assert.Empty(_fixture.CapturingTextWriter.Lines);
}
[Fact(DisplayName = "Parallel executes single child activity and completes")]
public async Task Parallel_ExecutesSingleChild_AndCompletes()
{
// Arrange
var parallel = new Parallel(
new WriteLine("Single Activity")
);
// Act
var result = await _fixture.RunActivityAsync(parallel);
// Assert
var journal = result.Journal;
var parallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity is Parallel);
Assert.NotNull(parallelContext);
Assert.Equal(ActivityStatus.Completed, parallelContext.Status);
Assert.Single(_fixture.CapturingTextWriter.Lines);
Assert.Equal("Single Activity", _fixture.CapturingTextWriter.Lines.Single());
}
[Fact(DisplayName = "Parallel executes multiple different activity types")]
public async Task Parallel_ExecutesMixedActivityTypes_AndCompletes()
{
// Arrange
var parallel = new Parallel(
new WriteLine("First"),
new SetName("TestName"),
new WriteLine("Second")
);
// Act
var result = await _fixture.RunActivityAsync(parallel);
// Assert
var journal = result.Journal;
var parallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity is Parallel);
Assert.NotNull(parallelContext);
Assert.Equal(ActivityStatus.Completed, parallelContext.Status);
Assert.Equal(2, _fixture.CapturingTextWriter.Lines.Count);
Assert.Contains("First", _fixture.CapturingTextWriter.Lines);
Assert.Contains("Second", _fixture.CapturingTextWriter.Lines);
}
[Fact(DisplayName = "Parallel completes only after all children complete")]
public async Task Parallel_CompletesOnlyAfterAllChildrenComplete()
{
// Arrange
var parallel = new Parallel(
new WriteLine("Child 1"),
new WriteLine("Child 2"),
new WriteLine("Child 3"),
new WriteLine("Child 4")
);
// Act
var result = await _fixture.RunActivityAsync(parallel);
// Assert
var journal = result.Journal;
var parallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity is Parallel);
Assert.NotNull(parallelContext);
Assert.Equal(ActivityStatus.Completed, parallelContext.Status);
Assert.Equal(4, _fixture.CapturingTextWriter.Lines.Count);
}
[Fact(DisplayName = "Parallel executes nested Parallel activities")]
public async Task Parallel_ExecutesNestedParallel_AndCompletes()
{
// Arrange
var innerParallel = new Parallel(
new WriteLine("Inner 1"),
new WriteLine("Inner 2")
);
var outerParallel = new Parallel(
new WriteLine("Outer 1"),
innerParallel,
new WriteLine("Outer 2")
);
// Act
var result = await _fixture.RunActivityAsync(outerParallel);
// Assert
var journal = result.Journal;
var outerParallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity == outerParallel);
var innerParallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity == innerParallel);
Assert.NotNull(outerParallelContext);
Assert.NotNull(innerParallelContext);
Assert.Equal(ActivityStatus.Completed, outerParallelContext.Status);
Assert.Equal(ActivityStatus.Completed, innerParallelContext.Status);
Assert.Equal(4, _fixture.CapturingTextWriter.Lines.Count);
Assert.Contains("Outer 1", _fixture.CapturingTextWriter.Lines);
Assert.Contains("Outer 2", _fixture.CapturingTextWriter.Lines);
Assert.Contains("Inner 1", _fixture.CapturingTextWriter.Lines);
Assert.Contains("Inner 2", _fixture.CapturingTextWriter.Lines);
}
[Fact(DisplayName = "Parallel remains in Running state when a child activity faults")]
public async Task Parallel_RemainsRunning_WhenChildFaults()
{
// Arrange
var parallel = new Parallel(
new WriteLine("Before Fault"),
new Fault { Message = new("Test fault") },
new WriteLine("After Fault")
);
// Act
var result = await _fixture.RunActivityAsync(parallel);
// Assert
var journal = result.Journal;
var parallelContext = journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity is Parallel);
Assert.NotNull(parallelContext);
Assert.Equal(ActivityStatus.Running, parallelContext.Status);
Assert.Equal(1, parallelContext.AggregateFaultCount);
// The non-faulted activities should still execute
Assert.Contains("Before Fault", _fixture.CapturingTextWriter.Lines);
Assert.Contains("After Fault", _fixture.CapturingTextWriter.Lines);
}
}