using StellaOps.TaskRunner.Core.Execution; using StellaOps.TaskRunner.Core.Planning; using StellaOps.TestKit; namespace StellaOps.TaskRunner.Tests; public sealed class PackRunStepStateMachineTests { private static readonly TaskPackPlanFailurePolicy RetryTwicePolicy = new(MaxAttempts: 3, BackoffSeconds: 5, ContinueOnError: false); [Trait("Category", TestCategories.Unit)] [Fact] public void Start_FromPending_SetsRunning() { var state = PackRunStepStateMachine.Create(); var started = PackRunStepStateMachine.Start(state, DateTimeOffset.UnixEpoch); Assert.Equal(PackRunStepExecutionStatus.Running, started.Status); Assert.Equal(0, started.Attempts); } [Trait("Category", TestCategories.Unit)] [Fact] public void CompleteSuccess_IncrementsAttempts() { var state = PackRunStepStateMachine.Create(); var running = PackRunStepStateMachine.Start(state, DateTimeOffset.UnixEpoch); var completed = PackRunStepStateMachine.CompleteSuccess(running, DateTimeOffset.UnixEpoch.AddSeconds(1)); Assert.Equal(PackRunStepExecutionStatus.Succeeded, completed.Status); Assert.Equal(1, completed.Attempts); Assert.Null(completed.NextAttemptAt); } [Trait("Category", TestCategories.Unit)] [Fact] public void RegisterFailure_SchedulesRetryUntilMaxAttempts() { var state = PackRunStepStateMachine.Create(); var running = PackRunStepStateMachine.Start(state, DateTimeOffset.UnixEpoch); var firstFailure = PackRunStepStateMachine.RegisterFailure(running, DateTimeOffset.UnixEpoch.AddSeconds(2), RetryTwicePolicy); Assert.Equal(PackRunStepFailureOutcome.Retry, firstFailure.Outcome); Assert.Equal(PackRunStepExecutionStatus.Pending, firstFailure.State.Status); Assert.Equal(1, firstFailure.State.Attempts); Assert.Equal(DateTimeOffset.UnixEpoch.AddSeconds(7), firstFailure.State.NextAttemptAt); var restarted = PackRunStepStateMachine.Start(firstFailure.State, DateTimeOffset.UnixEpoch.AddSeconds(7)); var secondFailure = PackRunStepStateMachine.RegisterFailure(restarted, DateTimeOffset.UnixEpoch.AddSeconds(9), RetryTwicePolicy); Assert.Equal(PackRunStepFailureOutcome.Retry, secondFailure.Outcome); Assert.Equal(2, secondFailure.State.Attempts); var finalStart = PackRunStepStateMachine.Start(secondFailure.State, DateTimeOffset.UnixEpoch.AddSeconds(9 + RetryTwicePolicy.BackoffSeconds)); var terminalFailure = PackRunStepStateMachine.RegisterFailure(finalStart, DateTimeOffset.UnixEpoch.AddSeconds(20), RetryTwicePolicy); Assert.Equal(PackRunStepFailureOutcome.Abort, terminalFailure.Outcome); Assert.Equal(PackRunStepExecutionStatus.Failed, terminalFailure.State.Status); Assert.Equal(3, terminalFailure.State.Attempts); Assert.Null(terminalFailure.State.NextAttemptAt); } [Trait("Category", TestCategories.Unit)] [Fact] public void Skip_FromPending_SetsSkipped() { var state = PackRunStepStateMachine.Create(); var skipped = PackRunStepStateMachine.Skip(state, DateTimeOffset.UnixEpoch.AddHours(1)); Assert.Equal(PackRunStepExecutionStatus.Skipped, skipped.Status); Assert.Equal(0, skipped.Attempts); } }