From 8a61fa4045d29878ced89b224ebd2d9e057cc681 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 22 Oct 2025 23:43:51 +0200 Subject: [PATCH] Add unit and integration tests for `SetOutput` activity - Introduced unit tests for `SetOutput` to validate single, multiple, and dynamic output scenarios. - Added integration tests covering workflows with `SetOutput` activity, ensuring correct output handling. - Simplified test setups by leveraging `ActivityTestFixture`. --- .../SetOutputTests.cs | 135 ++++++++++++++++++ .../CompositesPassingData/Workflows.cs | 8 +- .../Composition/SetOutputTests.cs | 114 +++++++++++++++ 3 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 test/integration/Elsa.Activities.IntegrationTests/SetOutputTests.cs create mode 100644 test/unit/Elsa.Activities.UnitTests/Composition/SetOutputTests.cs diff --git a/test/integration/Elsa.Activities.IntegrationTests/SetOutputTests.cs b/test/integration/Elsa.Activities.IntegrationTests/SetOutputTests.cs new file mode 100644 index 000000000..97e92b4a7 --- /dev/null +++ b/test/integration/Elsa.Activities.IntegrationTests/SetOutputTests.cs @@ -0,0 +1,135 @@ +using Elsa.Testing.Shared; +using Elsa.Workflows; +using Elsa.Workflows.Activities; +using Elsa.Workflows.Management.Activities.SetOutput; +using Xunit.Abstractions; + +namespace Elsa.Activities.IntegrationTests; + +public class SetOutputTests +{ + private readonly IServiceProvider _services; + private readonly CapturingTextWriter _capturingTextWriter = new(); + + public SetOutputTests(ITestOutputHelper testOutputHelper) + { + _services = new TestApplicationBuilder(testOutputHelper) + .WithCapturingTextWriter(_capturingTextWriter) + .AddWorkflow() + .AddWorkflow() + .AddWorkflow() + .Build(); + } + + [Fact(DisplayName = "SetOutput sets workflow execution context output")] + public async Task SetOutput_Should_Set_Workflow_Output() + { + // Arrange + await _services.PopulateRegistriesAsync(); + + // Act + var workflowState = await _services.RunWorkflowUntilEndAsync(); + + // Assert + Assert.Contains("Message", workflowState.Output.Keys); + Assert.Equal("Hello, World!", workflowState.Output["Message"]); + } + + [Fact(DisplayName = "SetOutput can set multiple outputs")] + public async Task SetOutput_Should_Set_Multiple_Outputs() + { + // Arrange + await _services.PopulateRegistriesAsync(); + + // Act + var workflowState = await _services.RunWorkflowUntilEndAsync(); + + // Assert + Assert.Contains("FirstName", workflowState.Output.Keys); + Assert.Contains("LastName", workflowState.Output.Keys); + Assert.Contains("Age", workflowState.Output.Keys); + + Assert.Equal("John", workflowState.Output["FirstName"]); + Assert.Equal("Doe", workflowState.Output["LastName"]); + Assert.Equal(30, workflowState.Output["Age"]); + } + + [Fact(DisplayName = "SetOutput handles dynamic output values")] + public async Task SetOutput_Should_Handle_Dynamic_Values() + { + // Arrange + await _services.PopulateRegistriesAsync(); + + // Act + var workflowState = await _services.RunWorkflowUntilEndAsync(); + + // Assert + Assert.Contains("Greeting", workflowState.Output.Keys); + var greeting = workflowState.Output["Greeting"] as string; + Assert.NotNull(greeting); + Assert.StartsWith("Hello at ", greeting); + } +} + +// Test Workflows + +/// +/// Basic workflow that uses SetOutput to set a workflow output +/// +public class BasicWorkflowOutputWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder workflow) + { + workflow.Root = new SetOutput + { + OutputName = new("Message"), + OutputValue = new("Hello, World!") + }; + } +} + +/// +/// Workflow that sets multiple outputs using SetOutput +/// +public class MultipleOutputsWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder workflow) + { + workflow.Root = new Sequence + { + Activities = + { + new SetOutput + { + OutputName = new("FirstName"), + OutputValue = new("John") + }, + new SetOutput + { + OutputName = new("LastName"), + OutputValue = new("Doe") + }, + new SetOutput + { + OutputName = new("Age"), + OutputValue = new(30) + } + } + }; + } +} + +/// +/// Workflow that uses dynamic values with SetOutput +/// +public class DynamicOutputValueWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder workflow) + { + workflow.Root = new SetOutput + { + OutputName = new("Greeting"), + OutputValue = new(context => $"Hello at {DateTime.UtcNow:yyyy-MM-dd HH:mm}") + }; + } +} diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs index 7e9c8273b..3afe999b2 100644 --- a/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs +++ b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs @@ -11,15 +11,15 @@ namespace Elsa.Workflows.IntegrationTests.Scenarios.CompositesPassingData; /// public class AddTextSubWorkflow : Composite { - public Input A { get; set; } = default!; - public Output B { get; set; } = default!; + public Input A { get; set; } = null!; + public Output B { get; set; } = null!; public AddTextSubWorkflow() { var setOutput = new SetOutput() { - OutputName = new Input("B"), - OutputValue = new Input(context => "hi there " + A.Get(context)) + OutputName = new("B"), + OutputValue = new(context => "hi there " + A.Get(context)) }; Root = new Sequence diff --git a/test/unit/Elsa.Activities.UnitTests/Composition/SetOutputTests.cs b/test/unit/Elsa.Activities.UnitTests/Composition/SetOutputTests.cs new file mode 100644 index 000000000..c1765cc7a --- /dev/null +++ b/test/unit/Elsa.Activities.UnitTests/Composition/SetOutputTests.cs @@ -0,0 +1,114 @@ +using Elsa.Testing.Shared; +using Elsa.Workflows; +using Elsa.Workflows.Management.Activities.SetOutput; + +namespace Elsa.Activities.UnitTests.Composition; + +public class SetOutputTests +{ + private const string DefaultOutputName = "Result"; + + [Theory] + [InlineData("test output")] + [InlineData("string value")] + [InlineData(42)] + [InlineData(true)] + [InlineData(3.14)] + [InlineData("")] + public async Task Should_Set_Workflow_Output_With_Value(object expectedValue) + { + // Act + var context = await ExecuteSetOutputAsync(DefaultOutputName, expectedValue); + + // Assert + AssertOutputEquals(context, DefaultOutputName, expectedValue); + } + + [Fact] + public async Task Should_Set_Workflow_Output_With_Null_Value() + { + // Act + var context = await ExecuteSetOutputAsync(DefaultOutputName, null); + + // Assert + Assert.True(context.WorkflowExecutionContext.Output.ContainsKey(DefaultOutputName)); + Assert.Null(context.WorkflowExecutionContext.Output[DefaultOutputName]); + } + + [Fact] + public async Task Should_Set_Output_With_Complex_Object() + { + // Arrange + var expectedValue = new { Name = "Test", Value = 42 }; + + // Act + var context = await ExecuteSetOutputAsync(DefaultOutputName, expectedValue); + + // Assert + AssertOutputEquals(context, DefaultOutputName, expectedValue); + } + + [Fact] + public async Task Should_Update_Workflow_Output_Multiple_Times() + { + // Arrange + const string outputName = "Counter"; + var firstValue = 1; + var secondValue = 2; + + // Act + var context1 = await ExecuteSetOutputAsync(outputName, firstValue); + var context2 = await ExecuteSetOutputAsync(outputName, secondValue, + ctx => ctx.WorkflowExecutionContext.Output[outputName] = firstValue); + + // Assert + AssertOutputEquals(context1, outputName, firstValue); + AssertOutputEquals(context2, outputName, secondValue); + } + + [Fact] + public async Task Should_Set_Different_Output_Names() + { + // Arrange + const string output1Name = "FirstOutput"; + const string output2Name = "SecondOutput"; + const string value1 = "value1"; + const int value2 = 42; + + // Act + var context1 = await ExecuteSetOutputAsync(output1Name, value1); + var context2 = await ExecuteSetOutputAsync(output2Name, value2, + ctx => ctx.WorkflowExecutionContext.Output[output1Name] = value1); + + // Assert + AssertOutputEquals(context1, output1Name, value1); + AssertOutputEquals(context2, output1Name, value1); + AssertOutputEquals(context2, output2Name, value2); + } + + private static async Task ExecuteSetOutputAsync( + string outputName, + object? outputValue, + Action? configureContext = null) + { + var setOutput = CreateSetOutputActivity(outputName, outputValue); + var fixture = new ActivityTestFixture(setOutput); + + if (configureContext != null) + fixture.ConfigureContext(configureContext); + + return await fixture.ExecuteAsync(); + } + + private static SetOutput CreateSetOutputActivity(string outputName, object? outputValue) => new() + { + OutputName = new(outputName), + OutputValue = new(outputValue) + }; + + private static void AssertOutputEquals(ActivityExecutionContext context, string outputName, object? expectedValue) + { + var actualValue = context.WorkflowExecutionContext.Output[outputName]; + Assert.Equal(expectedValue, actualValue); + } +}