fix: do not resume interrupted workflows that are already finished (#7435)
* Fix: do not resume interrupted workflows that are already finished * Avoid fixed timestamps in restart workflow test --------- Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
This commit is contained in:
parent
4eeb26feca
commit
02cd8085c6
|
|
@ -63,7 +63,8 @@ public class RestartInterruptedWorkflowsTask(
|
|||
return new()
|
||||
{
|
||||
IsExecuting = true,
|
||||
BeforeLastUpdated = cutoffTimestamp
|
||||
BeforeLastUpdated = cutoffTimestamp,
|
||||
WorkflowStatus = WorkflowStatus.Running,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
using Elsa.Common;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Options;
|
||||
using Elsa.Workflows.Runtime.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.GracefulShutdown;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for the <see cref="RestartInterruptedWorkflowsTask" />
|
||||
/// </summary>
|
||||
public class RestartInterruptedWorkflowsTests
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public RestartInterruptedWorkflowsTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper)
|
||||
.ConfigureElsa(elsa => elsa.UseWorkflowRuntime())
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Task ignores instances NOT Interrupted")]
|
||||
public async Task Filter()
|
||||
{
|
||||
var fakeRestarter = new RecordingRestarter();
|
||||
using var scope = _services.CreateScope();
|
||||
var instanceStore = scope.ServiceProvider.GetRequiredService<IWorkflowInstanceStore>();
|
||||
var staleTimestamp = GetStaleTimestamp(scope.ServiceProvider);
|
||||
|
||||
await SeedInstancesAsync(instanceStore, 3, WorkflowStatus.Running, WorkflowSubStatus.Executing, isExecuting: true, idPrefix: "stale-", timestamp: staleTimestamp);
|
||||
await SeedInstancesAsync(instanceStore, 2, WorkflowStatus.Finished, WorkflowSubStatus.Executing, isExecuting: true, idPrefix: "finished-stale-", timestamp: staleTimestamp);
|
||||
await SeedInstancesAsync(instanceStore, 1, WorkflowStatus.Finished, WorkflowSubStatus.Executing, isExecuting: false, idPrefix: "finished-", timestamp: staleTimestamp);
|
||||
|
||||
var scanner = ActivatorUtilities.CreateInstance<RestartInterruptedWorkflowsTask>(scope.ServiceProvider, fakeRestarter);
|
||||
await scanner.ExecuteAsync(CancellationToken.None);
|
||||
|
||||
Assert.Equal(3, fakeRestarter.RestartedIds.Count);
|
||||
Assert.All(fakeRestarter.RestartedIds, id => Assert.StartsWith("stale-", id));
|
||||
}
|
||||
|
||||
private static DateTimeOffset GetStaleTimestamp(IServiceProvider serviceProvider)
|
||||
{
|
||||
var clock = serviceProvider.GetRequiredService<ISystemClock>();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<RuntimeOptions>>().Value;
|
||||
return clock.UtcNow - options.InactivityThreshold - TimeSpan.FromMinutes(1);
|
||||
}
|
||||
|
||||
private static async Task SeedInstancesAsync(IWorkflowInstanceStore store, int count, WorkflowStatus status, WorkflowSubStatus subStatus, bool isExecuting, string idPrefix = "instance-", DateTimeOffset? timestamp = null)
|
||||
{
|
||||
var createdAt = timestamp ?? DateTimeOffset.UtcNow;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
await store.SaveAsync(new WorkflowInstance
|
||||
{
|
||||
Id = $"{idPrefix}{i}",
|
||||
DefinitionId = "def-1",
|
||||
DefinitionVersionId = "ver-1",
|
||||
Version = 1,
|
||||
Status = status,
|
||||
SubStatus = subStatus,
|
||||
IsExecuting = isExecuting,
|
||||
CreatedAt = createdAt,
|
||||
UpdatedAt = createdAt,
|
||||
WorkflowState = new State.WorkflowState
|
||||
{
|
||||
Id = $"{idPrefix}{i}",
|
||||
DefinitionId = "def-1",
|
||||
DefinitionVersionId = "ver-1",
|
||||
Status = status,
|
||||
SubStatus = subStatus,
|
||||
},
|
||||
}, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Captures restart calls without actually invoking the workflow runtime — keeps the integration test focused.</summary>
|
||||
private sealed class RecordingRestarter : IWorkflowRestarter
|
||||
{
|
||||
public List<string> RestartedIds { get; } = [];
|
||||
|
||||
public Task RestartWorkflowAsync(string workflowInstanceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
RestartedIds.Add(workflowInstanceId);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue