Add unit and integration tests for Fault activity to validate fault propagation behavior (#7113)
- Introduced multiple workflows (`FaultWorkflow`, `FaultWithDefaultsWorkflow`, `FaultViaFactoryWorkflow`, `FaultInSequenceWorkflow`) to test fault scenarios. - Added `FaultTests` to verify fault propagation, stopping subsequent activities, and validating default, custom, and factory-configured fault behaviors. - Included unit tests to ensure `FaultException` is thrown correctly with all properties set and default values applied when inputs are null.
This commit is contained in:
parent
74f06914de
commit
8f35cab2ad
|
|
@ -0,0 +1,84 @@
|
|||
using Elsa.Activities.IntegrationTests.Primitives.Workflows;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Exceptions;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.State;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for the <see cref="Fault"/> activity.
|
||||
/// </summary>
|
||||
public class FaultTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly WorkflowTestFixture _fixture = new WorkflowTestFixture(testOutputHelper)
|
||||
.AddWorkflow<FaultWorkflow>()
|
||||
.AddWorkflow<FaultWithDefaultsWorkflow>()
|
||||
.AddWorkflow<FaultInSequenceWorkflow>()
|
||||
.AddWorkflow<FaultViaFactoryWorkflow>();
|
||||
|
||||
[Fact(DisplayName = "Fault activity faults workflow execution")]
|
||||
public async Task Fault_Activity_Faults_Workflow_Execution()
|
||||
{
|
||||
// Act
|
||||
var workflowState = await _fixture.RunWorkflowAsync(FaultWorkflow.DefinitionId);
|
||||
|
||||
// Assert
|
||||
AssertWorkflowFaulted(workflowState);
|
||||
var incident = GetSingleIncident(workflowState);
|
||||
Assert.Equal("Test fault message", incident.Message);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Fault activity with default values faults workflow")]
|
||||
public async Task Fault_With_Default_Values_Faults_Workflow()
|
||||
{
|
||||
// Act
|
||||
var workflowState = await _fixture.RunWorkflowAsync(FaultWithDefaultsWorkflow.DefinitionId);
|
||||
|
||||
// Assert
|
||||
AssertWorkflowFaulted(workflowState);
|
||||
GetSingleIncident(workflowState);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Fault in sequence stops subsequent activities")]
|
||||
public async Task Fault_In_Sequence_Stops_Subsequent_Activities()
|
||||
{
|
||||
// Act
|
||||
var workflowState = await _fixture.RunWorkflowAsync(FaultInSequenceWorkflow.DefinitionId);
|
||||
|
||||
// Assert
|
||||
AssertWorkflowFaulted(workflowState);
|
||||
|
||||
var lines = _fixture.CapturingTextWriter.Lines.ToList();
|
||||
Assert.Contains("Before fault", lines);
|
||||
Assert.DoesNotContain("After fault - should not execute", lines);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Fault with factory method creates correct exception")]
|
||||
public async Task Fault_Created_With_Factory_Method()
|
||||
{
|
||||
// Act
|
||||
var workflowState = await _fixture.RunWorkflowAsync(FaultViaFactoryWorkflow.DefinitionId);
|
||||
|
||||
// Assert
|
||||
AssertWorkflowFaulted(workflowState);
|
||||
var incident = GetSingleIncident(workflowState);
|
||||
Assert.Equal("Created via factory", incident.Message);
|
||||
}
|
||||
|
||||
private static void AssertWorkflowFaulted(WorkflowState workflowState)
|
||||
{
|
||||
Assert.Equal(WorkflowSubStatus.Faulted, workflowState.SubStatus);
|
||||
}
|
||||
|
||||
private static ActivityIncident GetSingleIncident(WorkflowState workflowState)
|
||||
{
|
||||
var incident = Assert.Single(workflowState.Incidents);
|
||||
Assert.NotNull(incident.Exception);
|
||||
Assert.Equal(typeof(FaultException), incident.Exception.Type);
|
||||
return incident;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests.Primitives.Workflows;
|
||||
|
||||
public class FaultInSequenceWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = nameof(FaultInSequenceWorkflow);
|
||||
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
workflow.DefinitionId = DefinitionId;
|
||||
workflow.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine("Before fault"),
|
||||
new Fault
|
||||
{
|
||||
Code = new("SEQ_001"),
|
||||
Category = new("Sequence"),
|
||||
FaultType = new("Test"),
|
||||
Message = new("Fault in sequence")
|
||||
},
|
||||
new WriteLine("After fault - should not execute")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests.Primitives.Workflows;
|
||||
|
||||
public class FaultViaFactoryWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = nameof(FaultViaFactoryWorkflow);
|
||||
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
workflow.DefinitionId = DefinitionId;
|
||||
workflow.Root = Fault.Create("FACTORY_001", "Factory", "Integration", "Created via factory");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests.Primitives.Workflows;
|
||||
|
||||
public class FaultWithDefaultsWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = nameof(FaultWithDefaultsWorkflow);
|
||||
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
workflow.DefinitionId = DefinitionId;
|
||||
workflow.Root = new Fault
|
||||
{
|
||||
Code = new((string)null!),
|
||||
Category = new((string)null!),
|
||||
FaultType = new((string)null!),
|
||||
Message = new((string?)null)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests.Primitives.Workflows;
|
||||
|
||||
public class FaultWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = nameof(FaultWorkflow);
|
||||
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
workflow.DefinitionId = DefinitionId;
|
||||
workflow.Root = new Fault
|
||||
{
|
||||
Code = new("ERR_001"),
|
||||
Category = new("Test"),
|
||||
FaultType = new("Business"),
|
||||
Message = new("Test fault message")
|
||||
};
|
||||
}
|
||||
}
|
||||
128
test/unit/Elsa.Activities.UnitTests/Primitives/FaultTests.cs
Normal file
128
test/unit/Elsa.Activities.UnitTests/Primitives/FaultTests.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows;
|
||||
using Elsa.Workflows.Exceptions;
|
||||
|
||||
namespace Elsa.Activities.UnitTests.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="Fault"/> activity.
|
||||
/// </summary>
|
||||
public class FaultTests
|
||||
{
|
||||
[Fact(DisplayName = "Fault throws FaultException with all properties set")]
|
||||
public async Task Should_Throw_FaultException_With_All_Properties()
|
||||
{
|
||||
// Arrange
|
||||
var fault = new Fault
|
||||
{
|
||||
Code = new("ERR_001"),
|
||||
Category = new("HTTP"),
|
||||
FaultType = new("Business"),
|
||||
Message = new("Invalid request")
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var exception = await ExecuteAndAssertFaultAsync(fault);
|
||||
Assert.Equal("ERR_001", exception.Code);
|
||||
Assert.Equal("HTTP", exception.Category);
|
||||
Assert.Equal("Business", exception.Type);
|
||||
Assert.Equal("Invalid request", exception.Message);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Fault uses default values when inputs are null")]
|
||||
public async Task Should_Use_Default_Values_When_Inputs_Are_Null()
|
||||
{
|
||||
// Arrange
|
||||
var fault = new Fault
|
||||
{
|
||||
Code = new((string)null!),
|
||||
Category = new((string)null!),
|
||||
FaultType = new((string)null!),
|
||||
Message = new((string?)null)
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var exception = await ExecuteAndAssertFaultAsync(fault);
|
||||
Assert.Equal("0", exception.Code);
|
||||
Assert.Equal("General", exception.Category);
|
||||
Assert.Equal("System", exception.Type);
|
||||
Assert.NotNull(exception.Message);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Fault throws FaultException with null message")]
|
||||
public async Task Should_Throw_FaultException_With_Null_Message()
|
||||
{
|
||||
// Arrange
|
||||
var fault = new Fault
|
||||
{
|
||||
Code = new("ERR_002"),
|
||||
Category = new("Database"),
|
||||
FaultType = new("Integration"),
|
||||
Message = new((string?)null)
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var exception = await ExecuteAndAssertFaultAsync(fault);
|
||||
Assert.Equal("ERR_002", exception.Code);
|
||||
Assert.Equal("Database", exception.Category);
|
||||
Assert.Equal("Integration", exception.Type);
|
||||
Assert.NotNull(exception.Message);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "Fault throws FaultException with various code values")]
|
||||
[InlineData("404", "404")]
|
||||
[InlineData("VALIDATION_ERROR", "VALIDATION_ERROR")]
|
||||
[InlineData("", "")]
|
||||
public async Task Should_Throw_FaultException_With_Various_Codes(string code, string expectedCode)
|
||||
{
|
||||
// Arrange
|
||||
var fault = new Fault
|
||||
{
|
||||
Code = new(code),
|
||||
Category = new("Test"),
|
||||
FaultType = new("Test")
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var exception = await ExecuteAndAssertFaultAsync(fault);
|
||||
Assert.Equal(expectedCode, exception.Code);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Fault.Create factory method creates correctly configured instance")]
|
||||
public async Task Create_Factory_Method_Creates_Correctly_Configured_Instance()
|
||||
{
|
||||
// Arrange
|
||||
var fault = Fault.Create("CREATE_001", "Factory", "Test", "Created via factory");
|
||||
|
||||
// Act & Assert
|
||||
var exception = await ExecuteAndAssertFaultAsync(fault);
|
||||
Assert.Equal("CREATE_001", exception.Code);
|
||||
Assert.Equal("Factory", exception.Category);
|
||||
Assert.Equal("Test", exception.Type);
|
||||
Assert.Equal("Created via factory", exception.Message);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Fault.Create factory method with null message")]
|
||||
public async Task Create_Factory_Method_With_Null_Message()
|
||||
{
|
||||
// Arrange
|
||||
var fault = Fault.Create("CODE", "Category", "Type");
|
||||
|
||||
// Act & Assert
|
||||
var exception = await ExecuteAndAssertFaultAsync(fault);
|
||||
Assert.Equal("CODE", exception.Code);
|
||||
Assert.Equal("Category", exception.Category);
|
||||
Assert.Equal("Type", exception.Type);
|
||||
Assert.NotNull(exception.Message);
|
||||
}
|
||||
|
||||
private static async Task<FaultException> ExecuteAndAssertFaultAsync(IActivity activity)
|
||||
{
|
||||
return await Assert.ThrowsAsync<FaultException>(() => ExecuteAsync(activity));
|
||||
}
|
||||
|
||||
private static async Task<ActivityExecutionContext> ExecuteAsync(IActivity activity)
|
||||
{
|
||||
return await new ActivityTestFixture(activity).ExecuteAsync();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue