using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Runtime;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.GracefulShutdown;
///
/// Verifies that the workflow execution pipeline registers a with
/// for the lifetime of a single workflow execution cycle — the property the drain orchestrator depends on.
///
public class ExecutionCycleTrackingMiddlewareTests
{
private readonly IServiceProvider _services;
private readonly IWorkflowRunner _workflowRunner;
private readonly IExecutionCycleRegistry _cycleRegistry;
public ExecutionCycleTrackingMiddlewareTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.ConfigureElsa(elsa => elsa.UseWorkflowRuntime())
.Build();
_workflowRunner = _services.GetRequiredService();
_cycleRegistry = _services.GetRequiredService();
}
[Fact(DisplayName = "ActiveCount returns to zero after a workflow execution cycle completes")]
public async Task ActiveCountReturnsToZero()
{
await _services.PopulateRegistriesAsync();
Assert.Equal(0, _cycleRegistry.ActiveCount);
var workflow = new TestWorkflow(builder => builder.Root = new WriteLine("hello"));
await _workflowRunner.RunAsync(workflow);
Assert.Equal(0, _cycleRegistry.ActiveCount);
}
[Fact(DisplayName = "ExecutionCycleRegistry tracks an active handle while the execution cycle is running")]
public async Task ExecutionCycleRegistryTracksActiveCycle()
{
await _services.PopulateRegistriesAsync();
var observedActiveCounts = new List();
var observingActivity = new ObservingActivity(_cycleRegistry, observedActiveCounts);
var workflow = new TestWorkflow(builder => builder.Root = observingActivity);
await _workflowRunner.RunAsync(workflow);
// The activity executed inside the execution cycle — when it ran, the registry should have shown ≥ 1 active execution cycle.
Assert.NotEmpty(observedActiveCounts);
Assert.All(observedActiveCounts, count => Assert.True(count >= 1, $"Expected ≥ 1 active execution cycle during execution; saw {count}."));
// After the execution cycle completes, count is back to zero.
Assert.Equal(0, _cycleRegistry.ActiveCount);
}
/// An activity that records the execution cycle registry's active count when it executes.
private sealed class ObservingActivity(IExecutionCycleRegistry registry, List observed) : CodeActivity
{
protected override ValueTask ExecuteAsync(ActivityExecutionContext context)
{
observed.Add(registry.ActiveCount);
return ValueTask.CompletedTask;
}
}
}