Improvements edge cases for While activity unit tests

This commit is contained in:
lucas.hipolito 2025-10-27 15:23:58 +01:00
parent a28fbd2f2c
commit 5bb39bc2fe

View file

@ -1,5 +1,7 @@
using Elsa.Testing.Shared;
using Elsa.Workflows;
using Elsa.Workflows.Behaviors;
using Elsa.Workflows.Exceptions;
using NSubstitute;
namespace Elsa.Activities.UnitTests.Looping;
@ -62,8 +64,160 @@ public class WhileTests
var hasBodyScheduledActivity = context.HasScheduledActivity(bodyActivity);
Assert.True(hasBodyScheduledActivity);
}
[Fact]
public async Task Should_Throw_When_Condition_Is_Not_Set()
{
// Arrange
var body = new MockBodyActivity();
var whileActivity = new While(body)
{
// Reset condition to default/unset state
Condition = new Input<bool>(false) // This will be the default, but let's test the actual execution behavior
};
// Act
var context = await ExecuteAsync(whileActivity);
// Assert - with condition false, body should not be scheduled
Assert.False(context.HasScheduledActivity(body));
}
[Fact]
public async Task Should_Throw_InputEvaluationException_WhenExceptionFromCondition_And_Not_Schedule_Body()
{
// Arrange
var body = new MockBodyActivity();
var whileActivity = new While(
condition: new Input<bool>((Func<bool>)(() => throw new ApplicationException("boom"))),
body: body
);
// Act
// Any exception from condition evaluation throws InputEvaluationException
var ex = await Assert.ThrowsAsync<InputEvaluationException>(() => ExecuteAsync(whileActivity));
// Assert
Assert.Contains("Failed to evaluate activity input", ex.Message);
}
[Fact]
public async Task BodyIsNull_DoesNotSchedule()
{
// Arrange
var whileActivity = new While(
condition: new Input<bool>(true),
body: null);
// Helper methods
// Act
var context = await ExecuteAsync(whileActivity);
// Assert
var scheduled = context.WorkflowExecutionContext.Scheduler.List().ToList();
Assert.Empty(scheduled);
}
[Fact]
public async Task BodyThrows_ActivitySchedules_WithoutBreaking()
{
// Arrange
var body = new ThrowingBody(new InvalidOperationException("boom"));
var whileActivity = new While(
condition: new Input<bool>(true),
body: body
);
// Act + Assert
var context = await ExecuteAsync(whileActivity);
Assert.NotNull(context);
Assert.True(context.HasScheduledActivity(body));
}
[Fact]
public async Task Should_Use_Latest_Captured_State_When_Evaluating_Condition()
{
// Arrange
var body = new MockBodyActivity();
// Use a class to hold mutable state that can be changed after While construction
var stateHolder = new StateHolder { ShouldContinue = false };
var whileActivity = new While(
condition: new Input<bool>((Func<bool>)(() => stateHolder.ShouldContinue)),
body: body
);
// Mutate after construction, before execution.
stateHolder.ShouldContinue = true;
// Act
var context = await ExecuteAsync(whileActivity);
// Assert
Assert.True(context.HasScheduledActivity(body));
}
[Fact]
public async Task ConditionFalseInitially_EvaluatesOnce_AndDoesNotSchedule()
{
// Arrange
var evals = 0;
var body = new MockBodyActivity();
var whileActivity = new While(
condition: new Input<bool>((Func<bool>)(() => { evals++; return false; })),
body: body
);
// Act
var context = await ExecuteAsync(whileActivity);
// Assert
Assert.Equal(1, evals);
Assert.False(context.HasScheduledActivity(body));
}
[Fact]
public async Task DoesNotScheduleBodyTwice_InSingleExecution()
{
// Arrange
var body = new MockBodyActivity();
var whileActivity = new While(new Input<bool>(true), body);
var context = await ExecuteAsync(whileActivity);
// Assert
var all = context.WorkflowExecutionContext.Scheduler.List()
.Where(x => x.Activity == body)
.ToList();
Assert.Single(all);
}
// Break behavior registered (parity with For).
[Fact]
public void VerifyBreakBehaviorIsRegistered()
{
// Arrange
var whileActivity = new While(body: null);
// Act
var breakBehavior = whileActivity.Behaviors.OfType<BreakBehavior>().FirstOrDefault();
// Assert
Assert.NotNull(breakBehavior);
}
private class MockBodyActivity : Activity {
protected override ValueTask ExecuteAsync(ActivityExecutionContext context) => ValueTask.CompletedTask;
}
private class ThrowingBody(Exception exception) : Activity
{
protected override ValueTask ExecuteAsync(ActivityExecutionContext context) => throw exception;
}
private class StateHolder
{
public bool ShouldContinue;
}
private static Task<ActivityExecutionContext> ExecuteAsync(IActivity activity)
{
return new ActivityTestFixture(activity).ExecuteAsync();