feat: Enhance Task Runner with simulation and failure policy support
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Added tests for output projection and failure policy population in TaskPackPlanner.
- Introduced new failure policy manifest in TestManifests.
- Implemented simulation endpoints in the web service for task execution.
- Created TaskRunnerServiceOptions for configuration management.
- Updated appsettings.json to include TaskRunner configuration.
- Enhanced PackRunWorkerService to handle execution graphs and state management.
- Added support for parallel execution and conditional steps in the worker service.
- Updated documentation to reflect new features and changes in execution flow.
This commit is contained in:
master
2025-11-04 19:05:50 +02:00
parent 2eb6852d34
commit 3bd0955202
83 changed files with 15161 additions and 10678 deletions

View File

@@ -0,0 +1,105 @@
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TaskRunner.Infrastructure.Execution;
namespace StellaOps.TaskRunner.Tests;
public sealed class FilePackRunStateStoreTests
{
[Fact]
public async Task SaveAndGetAsync_RoundTripsState()
{
var directory = CreateTempDirectory();
try
{
var store = new FilePackRunStateStore(directory);
var original = CreateState("run:primary");
await store.SaveAsync(original, CancellationToken.None);
var reloaded = await store.GetAsync("run:primary", CancellationToken.None);
Assert.NotNull(reloaded);
Assert.Equal(original.RunId, reloaded!.RunId);
Assert.Equal(original.PlanHash, reloaded.PlanHash);
Assert.Equal(original.FailurePolicy, reloaded.FailurePolicy);
Assert.Equal(original.Steps.Count, reloaded.Steps.Count);
var step = Assert.Single(reloaded.Steps);
Assert.Equal("step-a", step.Key);
Assert.Equal(original.Steps["step-a"], step.Value);
}
finally
{
TryDelete(directory);
}
}
[Fact]
public async Task ListAsync_ReturnsStatesInDeterministicOrder()
{
var directory = CreateTempDirectory();
try
{
var store = new FilePackRunStateStore(directory);
var stateB = CreateState("run-b");
var stateA = CreateState("run-a");
await store.SaveAsync(stateB, CancellationToken.None);
await store.SaveAsync(stateA, CancellationToken.None);
var states = await store.ListAsync(CancellationToken.None);
Assert.Collection(states,
first => Assert.Equal("run-a", first.RunId),
second => Assert.Equal("run-b", second.RunId));
}
finally
{
TryDelete(directory);
}
}
private static PackRunState CreateState(string runId)
{
var failurePolicy = new TaskPackPlanFailurePolicy(MaxAttempts: 3, BackoffSeconds: 30, ContinueOnError: false);
var steps = new Dictionary<string, PackRunStepStateRecord>(StringComparer.Ordinal)
{
["step-a"] = new PackRunStepStateRecord(
StepId: "step-a",
Kind: PackRunStepKind.Run,
Enabled: true,
ContinueOnError: false,
MaxParallel: null,
ApprovalId: null,
GateMessage: null,
Status: PackRunStepExecutionStatus.Pending,
Attempts: 1,
LastTransitionAt: DateTimeOffset.UtcNow,
NextAttemptAt: null,
StatusReason: null)
};
return PackRunState.Create(runId, "hash-123", failurePolicy, steps, DateTimeOffset.UtcNow);
}
private static string CreateTempDirectory()
{
var path = Path.Combine(Path.GetTempPath(), "stellaops-taskrunner-tests", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(path);
return path;
}
private static void TryDelete(string directory)
{
try
{
if (Directory.Exists(directory))
{
Directory.Delete(directory, recursive: true);
}
}
catch
{
// Swallow cleanup errors to avoid masking test assertions.
}
}
}