Fix Switch activity behavior and add testing utilities for RunWorkflowResult and improve (#7151)
* Add testing utilities for `RunWorkflowResult` and improve `Switch` activity behavior - Introduced `RunWorkflowResultAssertions` for xUnit-based workflow testing. - Added `RunWorkflowResultExtensions` for journal-based activity execution context handling. - Refactored `Switch` activity to track scheduled activities and correctly complete execution when all scheduled activities finish. - Marked `RunActivityExtensions` methods as obsolete, recommending the use of `WorkflowTestFixture`. * Update src/common/Elsa.Testing.Shared.Integration/RunActivityExtensions.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/modules/Elsa.Workflows.Core/Activities/Switch.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add XML documentation to Switch activity callback method (#7152) * Initial plan * Add XML documentation to OnChildActivityCompletedAsync method Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>
This commit is contained in:
parent
48b574a411
commit
fbe9ca64f0
|
|
@ -10,36 +10,41 @@ namespace Elsa.Testing.Shared;
|
|||
/// Provides extension methods for <see cref="IServiceProvider"/>.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[Obsolete("Use WorkflowTestFixture instead.")]
|
||||
public static class RunActivityExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs the specified activity.
|
||||
/// </summary>
|
||||
/// <param name="services">The service provider.</param>
|
||||
/// <param name="activity">The activity to run.</param>
|
||||
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||
/// <returns>The result of running the activity.</returns>
|
||||
public static async Task<RunWorkflowResult> RunActivityAsync(this IServiceProvider services, IActivity activity, CancellationToken cancellationToken = default)
|
||||
extension(IServiceProvider services)
|
||||
{
|
||||
await services.PopulateRegistriesAsync();
|
||||
var workflowRunner = services.GetRequiredService<IWorkflowRunner>();
|
||||
var result = await workflowRunner.RunAsync(activity, cancellationToken: cancellationToken);
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// Runs the specified activity.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to run.</param>
|
||||
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||
/// <returns>The result of running the activity.</returns>
|
||||
[Obsolete("Use WorkflowTestFixture instead.")]
|
||||
public async Task<RunWorkflowResult> RunActivityAsync(IActivity activity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await services.PopulateRegistriesAsync();
|
||||
var workflowRunner = services.GetRequiredService<IWorkflowRunner>();
|
||||
var result = await workflowRunner.RunAsync(activity, cancellationToken: cancellationToken);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the specified activity.
|
||||
/// </summary>
|
||||
/// <param name="services">The service provider.</param>
|
||||
/// <param name="activity">The activity to run.</param>
|
||||
/// <param name="options">An set of options.</param>
|
||||
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||
/// <returns>The result of running the activity.</returns>
|
||||
public static async Task<RunWorkflowResult> RunActivityAsync(this IServiceProvider services, IActivity activity, RunWorkflowOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await services.PopulateRegistriesAsync();
|
||||
var workflowRunner = services.GetRequiredService<IWorkflowRunner>();
|
||||
var result = await workflowRunner.RunAsync(activity, options, cancellationToken);
|
||||
return result;
|
||||
/// <summary>
|
||||
/// Runs the specified activity.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to run.</param>
|
||||
/// <param name="options">A set of options.</param>
|
||||
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||
/// <returns>The result of running the activity.</returns>
|
||||
[Obsolete("Use WorkflowTestFixture instead.")]
|
||||
public async Task<RunWorkflowResult> RunActivityAsync(IActivity activity, RunWorkflowOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await services.PopulateRegistriesAsync();
|
||||
var workflowRunner = services.GetRequiredService<IWorkflowRunner>();
|
||||
var result = await workflowRunner.RunAsync(activity, options, cancellationToken);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Options;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Filters;
|
||||
using Elsa.Workflows.Runtime.Messages;
|
||||
using Elsa.Workflows.State;
|
||||
using JetBrains.Annotations;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Models;
|
||||
using JetBrains.Annotations;
|
||||
using Xunit;
|
||||
|
||||
namespace Elsa.Testing.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Provides assertion methods for <see cref="RunWorkflowResult"/> to facilitate Journal-based testing.
|
||||
/// These methods integrate with xUnit's assertion framework.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static class RunWorkflowResultAssertions
|
||||
{
|
||||
/// <param name="result">The workflow result.</param>
|
||||
extension(RunWorkflowResult result)
|
||||
{
|
||||
/// <summary>
|
||||
/// Asserts that the specified activity was executed (present in the journal).
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity that should have been executed.</param>
|
||||
public void AssertActivityExecuted(IActivity activity)
|
||||
{
|
||||
var context = result.GetActivityContext(activity);
|
||||
Assert.NotNull(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the specified activity was not executed (not present in the journal).
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity that should not have been executed.</param>
|
||||
public void AssertActivityNotExecuted(IActivity activity)
|
||||
{
|
||||
var context = result.GetActivityContext(activity);
|
||||
Assert.Null(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the specified activity completed successfully.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity that should have completed.</param>
|
||||
public void AssertActivityCompleted(IActivity activity)
|
||||
{
|
||||
var context = result.GetActivityContext(activity);
|
||||
Assert.NotNull(context);
|
||||
Assert.Equal(ActivityStatus.Completed, context.Status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the specified activity has the expected status.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to check.</param>
|
||||
/// <param name="expectedStatus">The expected status.</param>
|
||||
public void AssertActivityStatus(IActivity activity, ActivityStatus expectedStatus)
|
||||
{
|
||||
var context = result.GetActivityContext(activity);
|
||||
Assert.NotNull(context);
|
||||
Assert.Equal(expectedStatus, context.Status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that all specified activities were executed.
|
||||
/// </summary>
|
||||
/// <param name="activities">The activities that should have been executed.</param>
|
||||
public void AssertActivitiesExecuted(params IActivity[] activities)
|
||||
{
|
||||
foreach (var activity in activities)
|
||||
result.AssertActivityExecuted(activity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that all specified activities were not executed.
|
||||
/// </summary>
|
||||
/// <param name="activities">The activities that should not have been executed.</param>
|
||||
public void AssertActivitiesNotExecuted(params IActivity[] activities)
|
||||
{
|
||||
foreach (var activity in activities)
|
||||
result.AssertActivityNotExecuted(activity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that all specified activities completed successfully.
|
||||
/// </summary>
|
||||
/// <param name="activities">The activities that should have completed.</param>
|
||||
public void AssertActivitiesCompleted(params IActivity[] activities)
|
||||
{
|
||||
foreach (var activity in activities)
|
||||
result.AssertActivityCompleted(activity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the specified activity was executed exactly the expected number of times.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to check.</param>
|
||||
/// <param name="expectedCount">The expected execution count.</param>
|
||||
public void AssertActivityExecutionCount(IActivity activity, int expectedCount)
|
||||
{
|
||||
var actualCount = result.GetExecutionCount(activity);
|
||||
Assert.Equal(expectedCount, actualCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the workflow completed successfully.
|
||||
/// </summary>
|
||||
public void AssertWorkflowCompleted()
|
||||
{
|
||||
Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the workflow has the expected status and substatus.
|
||||
/// </summary>
|
||||
/// <param name="expectedStatus">The expected workflow status.</param>
|
||||
/// <param name="expectedSubStatus">The expected workflow substatus.</param>
|
||||
public void AssertWorkflowStatus(WorkflowStatus expectedStatus, WorkflowSubStatus expectedSubStatus)
|
||||
{
|
||||
Assert.Equal(expectedStatus, result.WorkflowState.Status);
|
||||
Assert.Equal(expectedSubStatus, result.WorkflowState.SubStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Models;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.Testing.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="RunWorkflowResult"/> to facilitate testing with Journal-based assertions.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public static class RunWorkflowResultExtensions
|
||||
{
|
||||
/// <param name="result">The workflow result.</param>
|
||||
extension(RunWorkflowResult result)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the activity execution context for the specified activity from the workflow result's journal.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to find.</param>
|
||||
/// <returns>The activity execution context, or null if not found.</returns>
|
||||
public ActivityExecutionContext? GetActivityContext(IActivity activity) => result.Journal.ActivityExecutionContexts.FirstOrDefault(x => x.Activity == activity);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified activity was executed (present in the journal).
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to check.</param>
|
||||
/// <returns>True if the activity was executed, false otherwise.</returns>
|
||||
public bool WasExecuted(IActivity activity) => result.GetActivityContext(activity) != null;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified activity completed successfully.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to check.</param>
|
||||
/// <returns>True if the activity completed, false otherwise.</returns>
|
||||
public bool WasCompleted(IActivity activity)
|
||||
{
|
||||
var context = result.GetActivityContext(activity);
|
||||
return context?.Status == ActivityStatus.Completed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution status of the specified activity.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to check.</param>
|
||||
/// <returns>The activity status, or null if the activity was not executed.</returns>
|
||||
public ActivityStatus? GetActivityStatus(IActivity activity) => result.GetActivityContext(activity)?.Status;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all executed activities from the workflow result's journal.
|
||||
/// </summary>
|
||||
/// <returns>Collection of executed activities.</returns>
|
||||
public IEnumerable<IActivity> GetExecutedActivities() => result.Journal.ActivityExecutionContexts.Select(x => x.Activity);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all completed activities from the workflow result's journal.
|
||||
/// </summary>
|
||||
/// <returns>Collection of completed activities.</returns>
|
||||
public IEnumerable<IActivity> GetCompletedActivities() =>
|
||||
result.Journal.ActivityExecutionContexts
|
||||
.Where(x => x.Status == ActivityStatus.Completed)
|
||||
.Select(x => x.Activity);
|
||||
|
||||
/// <summary>
|
||||
/// Counts how many times the specified activity was executed.
|
||||
/// Useful for activities that may execute multiple times (e.g., in loops).
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to count.</param>
|
||||
/// <returns>The number of times the activity was executed.</returns>
|
||||
public int GetExecutionCount(IActivity activity) =>
|
||||
result.Journal.ActivityExecutionContexts.Count(x => x.Activity == activity);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,7 @@ using Elsa.Workflows;
|
|||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Options;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Filters;
|
||||
using Elsa.Workflows.Runtime.Messages;
|
||||
using Elsa.Workflows.State;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
|
|
|||
|
|
@ -56,19 +56,37 @@ public class Switch : Activity
|
|||
var matchingCases = (await FindMatchingCasesAsync(context.ExpressionExecutionContext)).ToList();
|
||||
var hasAnyMatches = matchingCases.Any();
|
||||
var mode = context.Get(Mode);
|
||||
var results = mode == SwitchMode.MatchFirst ? hasAnyMatches ? new[] { matchingCases.First() } : Array.Empty<SwitchCase>() : matchingCases.ToArray();
|
||||
var results = mode == SwitchMode.MatchFirst
|
||||
? hasAnyMatches
|
||||
? new[] { matchingCases.First() }
|
||||
: Array.Empty<SwitchCase>()
|
||||
: matchingCases.ToArray();
|
||||
|
||||
var scheduledActivityIds = new HashSet<string>();
|
||||
|
||||
if (hasAnyMatches)
|
||||
{
|
||||
foreach (var result in results)
|
||||
{
|
||||
await context.ScheduleActivityAsync(result.Activity, OnChildActivityCompletedAsync);
|
||||
}
|
||||
if (result.Activity == null)
|
||||
continue;
|
||||
|
||||
return;
|
||||
await context.ScheduleActivityAsync(result.Activity, OnChildActivityCompletedAsync);
|
||||
scheduledActivityIds.Add(result.Activity.Id);
|
||||
}
|
||||
}
|
||||
else if (Default != null)
|
||||
{
|
||||
await context.ScheduleActivityAsync(Default, OnChildActivityCompletedAsync);
|
||||
scheduledActivityIds.Add(Default.Id);
|
||||
}
|
||||
|
||||
await context.ScheduleActivityAsync(Default, OnChildActivityCompletedAsync);
|
||||
context.SetProperty("ScheduledActivityIds", scheduledActivityIds);
|
||||
|
||||
if (!scheduledActivityIds.Any())
|
||||
{
|
||||
await context.CompleteActivityAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<SwitchCase>> FindMatchingCasesAsync(ExpressionExecutionContext context)
|
||||
|
|
@ -89,9 +107,19 @@ public class Switch : Activity
|
|||
return matchingCases;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the completion of scheduled child activities and completes the Switch activity only after all scheduled children have finished executing.
|
||||
/// </summary>
|
||||
private async ValueTask OnChildActivityCompletedAsync(ActivityCompletedContext context)
|
||||
{
|
||||
await context.TargetContext.CompleteActivityAsync();
|
||||
var scheduledActivityIds = context.TargetContext.GetProperty<HashSet<string>>("ScheduledActivityIds");
|
||||
|
||||
if (scheduledActivityIds != null
|
||||
&& scheduledActivityIds.Remove(context.ChildContext.Activity.Id)
|
||||
&& scheduledActivityIds.Count == 0)
|
||||
{
|
||||
await context.TargetContext.CompleteActivityAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,288 @@
|
|||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests.Branching;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for Switch activity.
|
||||
/// </summary>
|
||||
public class SwitchTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly WorkflowTestFixture _fixture = new(testOutputHelper);
|
||||
|
||||
[Fact(DisplayName = "Switch completes immediately when no matches and no activities scheduled")]
|
||||
public async Task Switch_CompletesImmediately_WhenNoMatchesAndNoActivitiesScheduled()
|
||||
{
|
||||
// Arrange
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
var case2Activity = new WriteLine("Case 2");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => false, case1Activity),
|
||||
new("Case 2", () => false, case2Activity)
|
||||
},
|
||||
Default = null
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivityCompleted(switchActivity);
|
||||
result.AssertActivitiesNotExecuted(case1Activity, case2Activity);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch completes immediately when no matches but has null default")]
|
||||
public async Task Switch_CompletesImmediately_WhenNoMatchesAndDefaultIsNull()
|
||||
{
|
||||
// Arrange
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => false, case1Activity)
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivityCompleted(switchActivity);
|
||||
result.AssertActivityNotExecuted(case1Activity);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch schedules default activity when no cases match")]
|
||||
public async Task Switch_SchedulesDefaultActivity_WhenNoCasesMatch()
|
||||
{
|
||||
// Arrange
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
var case2Activity = new WriteLine("Case 2");
|
||||
var defaultActivity = new WriteLine("Default");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => false, case1Activity),
|
||||
new("Case 2", () => false, case2Activity)
|
||||
},
|
||||
Default = defaultActivity
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, defaultActivity);
|
||||
result.AssertActivitiesNotExecuted(case1Activity, case2Activity);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch in MatchFirst mode schedules only first matching case")]
|
||||
public async Task Switch_InMatchFirstMode_SchedulesOnlyFirstMatchingCase()
|
||||
{
|
||||
// Arrange
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
var case2Activity = new WriteLine("Case 2");
|
||||
var case3Activity = new WriteLine("Case 3");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Mode = new(SwitchMode.MatchFirst),
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => false, case1Activity),
|
||||
new("Case 2", () => true, case2Activity),
|
||||
new("Case 3", () => true, case3Activity)
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, case2Activity);
|
||||
result.AssertActivitiesNotExecuted(case1Activity, case3Activity);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch in MatchAny mode schedules all matching cases")]
|
||||
public async Task Switch_InMatchAnyMode_SchedulesAllMatchingCases()
|
||||
{
|
||||
// Arrange
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
var case2Activity = new WriteLine("Case 2");
|
||||
var case3Activity = new WriteLine("Case 3");
|
||||
var case4Activity = new WriteLine("Case 4");
|
||||
var case5Activity = new WriteLine("Case 5");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Mode = new(SwitchMode.MatchAny),
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => false, case1Activity),
|
||||
new("Case 2", () => true, case2Activity),
|
||||
new("Case 3", () => true, case3Activity),
|
||||
new("Case 4", () => false, case4Activity),
|
||||
new("Case 5", () => true, case5Activity)
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, case2Activity, case3Activity, case5Activity);
|
||||
result.AssertActivitiesNotExecuted(case1Activity, case4Activity);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch completes only after all scheduled activities complete")]
|
||||
public async Task Switch_CompletesOnlyAfterAllScheduledActivitiesComplete()
|
||||
{
|
||||
// Arrange
|
||||
var activity1 = new WriteLine("Activity 1");
|
||||
var activity2 = new WriteLine("Activity 2");
|
||||
var activity3 = new WriteLine("Activity 3");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Mode = new(SwitchMode.MatchAny),
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => true, activity1),
|
||||
new("Case 2", () => true, activity2),
|
||||
new("Case 3", () => true, activity3)
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, activity1, activity2, activity3);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch completes after default activity completes")]
|
||||
public async Task Switch_CompletesAfterDefaultActivityCompletes()
|
||||
{
|
||||
// Arrange
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
var defaultActivity = new WriteLine("Default Activity");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => false, case1Activity)
|
||||
},
|
||||
Default = defaultActivity
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, defaultActivity);
|
||||
result.AssertActivityNotExecuted(case1Activity);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch with nested activities completes after all complete")]
|
||||
public async Task Switch_WithNestedActivities_CompletesAfterAllComplete()
|
||||
{
|
||||
// Arrange
|
||||
var nestedSequence1 = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine("Nested 1A"),
|
||||
new WriteLine("Nested 1B")
|
||||
}
|
||||
};
|
||||
|
||||
var nestedSequence2 = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine("Nested 2A"),
|
||||
new WriteLine("Nested 2B")
|
||||
}
|
||||
};
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Mode = new(SwitchMode.MatchAny),
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => true, nestedSequence1),
|
||||
new("Case 2", () => true, nestedSequence2)
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, nestedSequence1, nestedSequence2);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch in MatchFirst mode with single match")]
|
||||
public async Task Switch_InMatchFirstMode_WithSingleMatch()
|
||||
{
|
||||
// Arrange
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
var case2Activity = new WriteLine("Case 2");
|
||||
var case3Activity = new WriteLine("Case 3");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Mode = new(SwitchMode.MatchFirst),
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", () => false, case1Activity),
|
||||
new("Case 2", () => false, case2Activity),
|
||||
new("Case 3", () => true, case3Activity)
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, case3Activity);
|
||||
result.AssertActivitiesNotExecuted(case1Activity, case2Activity);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Switch evaluates conditions with expression context")]
|
||||
public async Task Switch_EvaluatesConditionsWithExpressionContext()
|
||||
{
|
||||
// Arrange
|
||||
var conditionValue = true;
|
||||
var case1Activity = new WriteLine("Case 1");
|
||||
var case2Activity = new WriteLine("Case 2");
|
||||
var case3Activity = new WriteLine("Case 3");
|
||||
|
||||
var switchActivity = new Switch
|
||||
{
|
||||
Cases =
|
||||
{
|
||||
new("Case 1", _ => false, case1Activity),
|
||||
new("Case 2", _ => conditionValue, case2Activity),
|
||||
new("Case 3", _ => false, case3Activity)
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _fixture.RunActivityAsync(switchActivity);
|
||||
|
||||
// Assert
|
||||
result.AssertActivitiesCompleted(switchActivity, case2Activity);
|
||||
result.AssertActivitiesNotExecuted(case1Activity, case3Activity);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue