elsa-core/src/common/Elsa.Testing.Shared.Integration/RunWorkflowExtensions.cs
Sipke Schoorstra fbe9ca64f0
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>
2025-12-19 17:05:12 +01:00

92 lines
4 KiB
C#

using Elsa.Common.Models;
using Elsa.Workflows;
using Elsa.Workflows.Management;
using Elsa.Workflows.Models;
using Elsa.Workflows.Options;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Messages;
using Elsa.Workflows.State;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Testing.Shared;
/// <summary>
/// Provides extension methods for <see cref="IServiceProvider"/>.
/// </summary>
[PublicAPI]
public static class RunWorkflowExtensions
{
/// <param name="services">The services.</param>
extension(IServiceProvider services)
{
/// <summary>
/// Runs a workflow until its end, automatically resuming any bookmark it encounters.
/// </summary>
/// <param name="workflowDefinitionId">The ID of the workflow definition.</param>
/// <param name="input">An optional dictionary of input values.</param>
/// <param name="correlationId">An optional correlation id of the workflow.</param>
/// <param name="versionOptions">An optional set of options to specify the version of the workflow definition to retrieve.</param>
/// <param name="runWorkflowOptions">Optional workflow execution options.</param>
/// <returns>The workflow state.</returns>
public async Task<WorkflowState> RunWorkflowUntilEndAsync(string workflowDefinitionId,
IDictionary<string, object>? input = null,
string? correlationId = null,
VersionOptions? versionOptions = null,
RunWorkflowOptions? runWorkflowOptions = null)
{
var workflowDefinitionService = services.GetRequiredService<IWorkflowDefinitionService>();
var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(workflowDefinitionId, versionOptions ?? VersionOptions.Published);
if (workflowGraph == null)
throw new InvalidOperationException($"Workflow definition with ID '{workflowDefinitionId}' not found.");
var workflowRuntime = services.GetRequiredService<IWorkflowRuntime>();
var workflowClient = await workflowRuntime.CreateClientAsync();
var response = await workflowClient.CreateAndRunInstanceAsync(new()
{
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionVersionId(workflowGraph.Workflow.Identity.Id),
Input = input,
CorrelationId = correlationId,
Properties = runWorkflowOptions?.Properties
});
var bookmarkStore = services.GetRequiredService<IBookmarkStore>();
// Continue resuming the workflow for as long as there are bookmarks to resume and the workflow is not Finished.
while (response.Status != WorkflowStatus.Finished)
{
var bookmarks = (await bookmarkStore.FindManyAsync(new()
{
WorkflowInstanceId = response.WorkflowInstanceId
})).ToList();
if (!bookmarks.Any())
break;
foreach (var bookmark in bookmarks)
{
var runRequest = new RunWorkflowInstanceRequest
{
BookmarkId = bookmark.Id,
Input = input,
Properties = runWorkflowOptions?.Properties
};
response = await workflowClient.RunInstanceAsync(runRequest);
}
}
// Return the workflow state.
return await workflowClient.ExportStateAsync();
}
/// <summary>
/// Runs a workflow until its end, automatically resuming any bookmark it encounters.
/// </summary>
public async Task<WorkflowState> RunWorkflowUntilEndAsync<TWorkflow>(IDictionary<string, object>? input = null) where TWorkflow : IWorkflow
{
var workflowDefinitionId = typeof(TWorkflow).Name;
return await services.RunWorkflowUntilEndAsync(workflowDefinitionId, input);
}
}
}