Adds integration tests for core activities (#7100)

* Add unit and integration tests for `DownloadHttpFile` activity

- Developed comprehensive unit tests for the `DownloadHttpFile` activity validating method execution (GET, POST), URL handling, headers, and status codes.
- Added integration tests to verify functionality like authorization headers, filename extraction, and response stream handling.
- Updated `IntegrationTests` project references to include the required `Elsa.Http` module.

* Refactor `DownloadHttpFileTests` to simplify test setup and improve reusability

- Consolidated test initialization logic into `RunActivityAsync` for cleaner and reusable test setup.
- Updated existing tests to use `RunActivityAsync`, reducing boilerplate code.
- Unified filename extraction tests into a parameterized theory to improve test coverage and clarity.
- Simplified helper handlers with expression-bodied members for readability.

* Add comments to clarify HttpResponseMessage disposal responsibility in `DownloadHttpFileTests`.

* Ensure `HttpResponseMessage.Content` is always set to prevent null reference issues in `DownloadHttpFileTests`.

* Refactor `DownloadHttpFileTests` to ensure `HttpResponseMessage.Content` is always initialized and simplify content assignment logic.

* Update `DownloadHttpFileTests` to set an empty `HttpResponseMessage.Content` for testing `HasContent` behavior

* Add `GitHubActionsTestLogger` package to test projects and refactor `Directory.Packages.props`

- Updated unit and integration test projects to include `GitHubActionsTestLogger` for improved test reporting in CI.
- Refactored `Directory.Packages.props` to add conditional dependencies for .NET 8, 9, and 10 compatibility.

* Remove `GitHubActionsTestLogger` package from test projects.

* Add project reference for unit tests and update `DownloadHttpFileTests`

- Included `Elsa.Activities.UnitTests` project reference in `IntegrationTests` to reuse helpers.
- Removed redundant `TestHttpMessageHandler` by utilizing shared helper from `UnitTests`.

* Refactor `DownloadHttpFileTests` to remove unused methods and simplify imports

* Update target framework to .NET 10 and upgrade `Nuke.Components` package to v10.0.0

* Add integration tests for smoke testing all core workflow activities

- Introduced `ActivitiesSmokeTests` to validate basic functionality of core workflow activities, including control flow and data manipulation.
- Added comprehensive `ActivitiesSmokeTestWorkflow` to test activities such as `Start`, `Finish`, `If`, `Switch`, `For`, `While`, `ForEach`, `SetVariable`, and `SetOutput`.

* Refactor `ActivitiesSmokeTestWorkflow` to reorder and clarify activity test cases

* Update test/integration/Elsa.Workflows.IntegrationTests/Scenarios/SmokeTests/ActivitiesSmokeTestWorkflow.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/integration/Elsa.Workflows.IntegrationTests/Scenarios/SmokeTests/ActivitiesSmokeTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Sipke Schoorstra 2025-11-24 14:43:42 +01:00 committed by GitHub
parent b0d93d347b
commit f020c5e9d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 318 additions and 0 deletions

View file

@ -0,0 +1,209 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Management.Activities.SetOutput;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.SmokeTests;
/// <summary>
/// Comprehensive smoke test workflow that exercises basic control flow and data manipulation activities.
/// Tests: Start, Sequence, Break, Complete, If, Switch, While, For, ForEach,
/// WriteLine, SetName, SetVariable&lt;T&gt;, SetVariable (untyped), SetOutput
/// </summary>
public class ActivitiesSmokeTestWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
// Variables for testing
var counter = new Variable<int>("Counter", 0);
var name = new Variable<string>("Name", "Initial");
var result = new Variable<string>("Result", "");
var items = new Variable<List<string>>("Items", ["A", "B", "C"]);
var currentItem = new Variable<string>("CurrentItem", "");
var loopCounter = new Variable<int>("LoopCounter", 0);
var untypedVar = new Variable<object?>("UntypedVar", null);
var switchValue = new Variable<int>("SwitchValue", 2);
workflow.WithVariables(counter, name, result, items, currentItem, loopCounter, untypedVar, switchValue);
workflow.Root = new Sequence
{
Activities =
{
// Test Start activity
new Start(),
// Test SetName
new SetName
{
Value = new("SmokeTestWorkflow")
},
// Test WriteLine
new WriteLine(context => "=== Smoke Test Started ==="),
// Test SetVariable<T>
new SetVariable<string>
{
Variable = name,
Value = new("Updated Name")
},
new WriteLine(context => $"Name: {name.Get(context)}"),
// Test SetVariable (untyped)
new SetVariable
{
Variable = untypedVar,
Value = new("Untyped value")
},
new WriteLine(context => $"Untyped: {untypedVar.Get(context)}"),
// Test If activity (condition true)
new If(() => true)
{
Then = new Sequence
{
Activities =
{
new WriteLine("If branch: True path executed"),
new SetVariable<int> { Variable = counter, Value = new(10) }
}
},
Else = new WriteLine("If branch: False path (should not execute)")
},
// Test Switch activity
new Switch
{
Cases =
{
new(
"Case1",
context => ValueTask.FromResult(switchValue.Get(context) == 1),
new WriteLine("Switch: Case 1 (should not execute)")
),
new(
"Case2",
context => ValueTask.FromResult(switchValue.Get(context) == 2),
new Sequence
{
Activities =
{
new WriteLine("Switch: Case 2 executed"),
new SetVariable<string> { Variable = result, Value = new("Switch-2") }
}
}
),
new(
"Case3",
context => ValueTask.FromResult(switchValue.Get(context) == 3),
new WriteLine("Switch: Case 3 (should not execute)")
)
},
Default = new WriteLine("Switch: Default (should not execute)")
},
// Test For loop with Break
new Sequence
{
Activities =
{
new WriteLine("For loop: Starting"),
new For
{
Start = new(0),
End = new(100),
Step = new(1),
Body = new Sequence
{
Activities =
{
new SetVariable<int> { Variable = loopCounter, Value = new(context => loopCounter.Get(context) + 1) },
// Break after 3 iterations
new If(context => loopCounter.Get(context) >= 3)
{
Then = new Break()
}
}
}
},
new WriteLine(context => $"For loop: Completed with {loopCounter.Get(context)} iterations")
}
},
// Test While loop with Break
new Sequence
{
Activities =
{
new SetVariable<int> { Variable = loopCounter, Value = new(0) },
new WriteLine("While loop: Starting"),
new While(() => true)
{
Body = new Sequence
{
Activities =
{
new SetVariable<int> { Variable = loopCounter, Value = new(context => loopCounter.Get(context) + 1) },
new WriteLine(context => $"While loop: Iteration {loopCounter.Get(context)}"),
// Break after 3 iterations
new If(context => loopCounter.Get(context) >= 3)
{
Then = new Break()
}
}
}
},
new WriteLine(context => $"While loop: Completed with {loopCounter.Get(context)} iterations")
}
},
// Test ForEach with Break
new Sequence
{
Activities =
{
new SetVariable<int> { Variable = loopCounter, Value = new(0) },
new WriteLine("ForEach loop: Starting"),
new ForEach<string>
{
Items = new(items),
CurrentValue = new(currentItem),
Body = new Sequence
{
Activities =
{
new SetVariable<int> { Variable = loopCounter, Value = new(context => loopCounter.Get(context) + 1) },
new WriteLine(context => $"ForEach: Item '{currentItem.Get(context)}'"),
// Break after processing 2 items (A, B)
new If(context => loopCounter.Get(context) >= 2)
{
Then = new Break()
}
}
}
},
new WriteLine(context => $"ForEach loop: Completed with {loopCounter.Get(context)} items processed")
}
},
// Test SetOutput
new SetOutput
{
OutputName = new("FinalResult"),
OutputValue = new(context => $"Counter={counter.Get(context)}, Name={name.Get(context)}, Result={result.Get(context)}")
},
// Test Finish and End activities
new Finish(),
new End(),
// Test Complete activity (ends workflow immediately)
new Complete(),
// This should not execute due to Complete
new WriteLine("After Complete (should not execute)")
}
};
}
}

View file

@ -0,0 +1,109 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.Models;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.SmokeTests;
/// <summary>
/// Smoke tests that verify basic functionality of core workflow activities.
/// </summary>
public class ActivitiesSmokeTests(ITestOutputHelper testOutputHelper)
{
private readonly WorkflowTestFixture _fixture = new(testOutputHelper);
[Fact(DisplayName = "Smoke test executes all core activities successfully")]
public async Task SmokeTest_ExecutesAllActivities_Successfully()
{
// Act
var (result, _) = await RunWorkflowAndCaptureOutput();
// Assert - Workflow completed successfully
Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
// Verify outputs were set correctly
var outputs = result.WorkflowState.Output;
Assert.NotNull(outputs);
Assert.True(outputs.TryGetValue("FinalResult", out var finalResultObj));
var finalResult = finalResultObj?.ToString();
Assert.NotNull(finalResult);
Assert.Contains("Counter=10", finalResult);
Assert.Contains("Name=Updated Name", finalResult);
Assert.Contains("Result=Switch-2", finalResult);
}
[Fact(DisplayName = "Smoke test verifies all activities were executed")]
public async Task SmokeTest_VerifiesActivityExecution()
{
// Act
var (_, lines) = await RunWorkflowAndCaptureOutput();
// Assert - Verify key activities executed by checking WriteLine outputs
Assert.Contains(lines, line => line.Contains("=== Smoke Test Started ==="));
Assert.Contains(lines, line => line.Contains("Name: Updated Name"));
Assert.Contains(lines, line => line.Contains("Untyped: Untyped value"));
Assert.Contains(lines, line => line.Contains("If branch: True path executed"));
Assert.Contains(lines, line => line.Contains("Switch: Case 2 executed"));
Assert.Contains(lines, line => line.Contains("For loop: Completed with 3 iterations"));
Assert.Contains(lines, line => line.Contains("While loop: Completed with 3 iterations"));
Assert.Contains(lines, line => line.Contains("ForEach loop: Completed with 2 items processed"));
// Verify activities after Complete did NOT execute
Assert.DoesNotContain(lines, line => line.Contains("After Complete (should not execute)"));
}
[Fact(DisplayName = "Break activity works correctly in different loop contexts")]
public async Task SmokeTest_BreakActivity_WorksInDifferentLoops()
{
// Act
var (_, lines) = await RunWorkflowAndCaptureOutput();
// Assert - Verify Break worked in For loop (stopped at 3 iterations, not 100)
Assert.Contains(lines, line => line.Contains("For loop: Completed with 3 iterations"));
// Verify Break worked in While loop (stopped at 3 iterations, didn't run infinitely)
Assert.Contains(lines, line => line.Contains("While loop: Iteration 1"));
Assert.Contains(lines, line => line.Contains("While loop: Iteration 2"));
Assert.Contains(lines, line => line.Contains("While loop: Iteration 3"));
Assert.Contains(lines, line => line.Contains("While loop: Completed with 3 iterations"));
// Verify Break worked in ForEach (stopped after 2 items: A, B, not C)
Assert.Contains(lines, line => line.Contains("ForEach: Item 'A'"));
Assert.Contains(lines, line => line.Contains("ForEach: Item 'B'"));
Assert.DoesNotContain(lines, line => line.Contains("ForEach: Item 'C'"));
Assert.Contains(lines, line => line.Contains("ForEach loop: Completed with 2 items processed"));
}
[Fact(DisplayName = "Switch activity executes correct case")]
public async Task SmokeTest_SwitchActivity_ExecutesCorrectCase()
{
// Act
var (_, lines) = await RunWorkflowAndCaptureOutput();
// Assert - Only Case 2 should execute
Assert.DoesNotContain(lines, line => line.Contains("Switch: Case 1 (should not execute)"));
Assert.Contains(lines, line => line.Contains("Switch: Case 2 executed"));
Assert.DoesNotContain(lines, line => line.Contains("Switch: Case 3 (should not execute)"));
Assert.DoesNotContain(lines, line => line.Contains("Switch: Default (should not execute)"));
}
[Fact(DisplayName = "If activity executes correct branch")]
public async Task SmokeTest_IfActivity_ExecutesCorrectBranch()
{
// Act
var (_, lines) = await RunWorkflowAndCaptureOutput();
// Assert - Only Then branch should execute
Assert.Contains(lines, line => line.Contains("If branch: True path executed"));
Assert.DoesNotContain(lines, line => line.Contains("If branch: False path (should not execute)"));
}
private async Task<(RunWorkflowResult Result, List<string> Lines)> RunWorkflowAndCaptureOutput()
{
var workflow = new ActivitiesSmokeTestWorkflow();
var result = await _fixture.RunWorkflowAsync(workflow);
var lines = _fixture.CapturingTextWriter.Lines.ToList();
return (result, lines);
}
}