elsa-core/test/unit/Elsa.Activities.UnitTests/Http/SendHttpRequestTests.cs
Sipke Schoorstra 762f23dbc3
Adds SetVariable activity unit tests (#6989)
* Update doc/qa/test-guidelines.md

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* Improvements on maintainability of sendhttprequest unit tests

* Improving tests and scheduled activity evaluation for activity context

* Refactor and splitting unnecessary grouped tests

* Improvements on SendHttp Unit tests

* Improvements on tests

* Update test/unit/Elsa.Activities.UnitTests/HTTP/SendHttpRequestTests.cs

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

* Small suggestions from copilot

* Update test/unit/Elsa.Activities.UnitTests/HTTP/SendHttpRequestTests.cs

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

* small copilot suggestion

* Introduce Scheduler Strategy Interfaces and Implementations for Workflow and Activity Execution Contexts

- Added `IWorkflowExecutionContextSchedulerStrategy` and `IActivityExecutionContextSchedulerStrategy` interfaces.
- Implemented `WorkflowExecutionContextSchedulerStrategy` and `ActivityExecutionContextSchedulerStrategy` for scheduling activities in workflows.
- Refactored scheduling logic to utilize the new scheduler strategies.
- Updated unit tests and test helpers to reflect refactoring, introducing fake implementations for testing purposes.
- Adjusted background execution scheduling and improved extensibility for custom scheduler strategies.

* Refactor `SendHttpRequestTests`: simplify scheduling assertions, use shared extensions, and standardize method naming. Streamline helper methods and remove unused test logic.

* Refactor: Replace `ActivityTestHelper` with `ActivityTestFixture` in unit tests for streamlined activity testing

- Introduced `ActivityTestFixture` with a fluent API for better test setup and execution of activities.
- Added extension methods `ActivityTestFixtureExtensions` and `ActivityTestFixtureHttpExtensions` for configuring attributes and HTTP services.
- Updated test guidelines and unit tests to use the new fixture and extensions.
- Removed `ActivityTestHelper`.

* Refactor: Move `ActivityTestFixture` and related extensions to shared project for reuse across test suites

- Consolidated `ActivityTestFixture`, `ActivityTestFixtureExtensions`, and `ActivityTestFixtureHttpExtensions` into `Elsa.Testing.Shared`.
- Updated namespaces and imports across unit tests to reflect new structure.
- Enhanced `AssertActivityAttributes` and added fluent configuration APIs.
- Adjusted `Directory.Packages.props` with new dependencies, including `NSubstitute` and `xunit.assert`.

* Refactor `SetVariableTests`: inline `ActivityTestFixture` initialization to simplify test setup.

* Refactor `ActivityTestFixture`: eliminate redundant field `_services`, add `UsedImplicitly` attributes, and improve service collection management

* Apply suggestion from @Copilot

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

* Add XML documentation for `ActivityExecutionContextExtensions`, detailing methods and parameters.

* Apply suggestion from @Copilot

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

* Add XML documentation for scheduler strategies and their methods

- Updated `IActivityExecutionContextSchedulerStrategy` and `IWorkflowExecutionContextSchedulerStrategy` interfaces with XML summaries for methods.
- Added XML documentation to implementations (`ActivityExecutionContextSchedulerStrategy`, `WorkflowExecutionContextSchedulerStrategy`) and test fakes for clarity.

* Refactor `WriteLineTests`: consolidate duplicate test logic, simplify setup with shared helper method, and enhance readability in assertions.

* Refactor `WriteLineTests` and `SetVariableTests`: replace `WriteLineAsync` assertions with synchronous `WriteLine`, streamline exception recording in `SetVariableTests`, and remove unused imports.

* Add integration and unit tests for `SetVariable` activity: ensure variable scoping, null handling, and value setting are covered.

* Add new `SetVariableOfTTests` and update `SetVariableTests` for improved test coverage and type handling.

* Add new unit and integration tests for activity input and expression evaluation

- Introduced comprehensive test suites covering activity input evaluation, expression handling, and fault scenarios.
- Added unit tests for `ExpressionDescriptorRegistry`, `ExpressionEvaluator`, and activity execution context extensions.
- Added integration tests: `CustomInputEvaluatorTests`, `InputEvaluationErrorTests`, and `InputPropertyEvaluationTests`.
- Enhanced code coverage for edge cases and async evaluation logic.

* Remove unused `RunWorkflowAsync` extension method from `RunActivityExtensions`.

* Rename `CustomInputEvaluatorTests` to `InputEvaluationTests` for consistency with naming conventions.

* Remove unused `Elsa.Workflows.Activities` import from `RunActivityExtensions`.

* Remove redundant comments from `InputEvaluationErrorTests` for clarity.

* Refactor unit tests to streamline activity and expression evaluations

- Refactored `ExecuteActivityAsync` and `ExecuteWriteLineAsync` into shared helpers for consistency and reuse across tests.
- Replaced redundant mock setups with helper methods in `ExpressionDescriptorRegistryTests`.
- Simplified test setup for expression and activity evaluation by removing unused imports and consolidating configuration logic.
- Enhanced readability by reducing duplicate code and leveraging shared utility methods.

* Refactor activity input evaluation tests

- Extracted `CreateContextAsync` helper into `EvaluationTestHelpers` for reuse across evaluation test suites.
- Replaced inline activity context setup with shared helper in `InputPropertyEvaluationTests`, `WrappedInputEvaluationTests`, and related test suites.
- Simplified test method names for clarity and consistency.
- Updated test annotations to enhance readability and align with naming conventions.

* Remove redundant test cases and unused imports

- Deleted duplicated and non-essential test cases across evaluation test suites.
- Removed unused imports to improve code cleanliness and readability.
- Streamlined variable initializations and method calls within test setups.

* Remove redundant test case from `InputEvaluationErrorTests`

- Deleted the `ContinuesEvaluationForMultipleInputs` test, as it overlaps with existing tests and does not provide additional coverage.

* Remove redundant assertion from `InputPropertyEvaluationTests`

- Deleted `Assert.True(context.GetHasEvaluatedProperties())`, as it is unnecessary for verifying test outcomes.

* Remove redundant test cases from `WrappedInputEvaluationTests`

- Deleted `UsesDefaultValueWhenInputIsNull` and `EvaluatesExpression` tests as they are either duplicated or unnecessary for current test coverage.

* Add unit test projects for `Elsa.Workflows.Management` and `Elsa.Expressions`

- Introduced new test projects to separate and organize unit tests for `Elsa.Workflows.Management` and `Elsa.Expressions`.
- Updated `Elsa.sln` to include references to the newly added test projects.
- Adjusted namespaces in affected test classes for consistency with the updated project structure.

* Refactor `ExpressionEvaluatorTests` for clarity and consistency

- Simplified test method names and annotations for improved readability.
- Replaced duplicate mock setups with helper functions (`CreateContextAsync`, `CreateContextWithMockHandlerAsync`, and related methods).
- Streamlined test setups by removing redundant code and consolidating context creation logic.
- Updated test annotations to include descriptive `DisplayName` attributes.

* Apply suggestion from @Copilot

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

* Remove redundant comment from `ExpressionEvaluatorTests` for clarity

* Remove redundant blank lines from unit test classes

- Eliminated unnecessary blank lines across `ActivityExecutionContextExtensions` test suites to improve code readability and consistency.

* Add unit tests for Flowchart activity

- Introduced `FlowchartTests` to verify common Flowchart behavior, including start activity scheduling, execution without a start activity, and `UseTokenFlow` handling.
- Added `FlowchartTestHelpers` to encapsulate shared test logic.
- Updated project file to include the new `Flow` folder for organization.

* Add integration tests for Flowchart activity execution strategies

- Introduced `FlowchartCounterBasedTests` and `FlowchartTokenBasedTests` to verify different Flowchart execution strategies.
- Added `FlowchartTestHelpers` for shared test logic, including helper methods for creating various flowchart configurations and connections.
- Enhanced test coverage with scenarios for linear, parallel, and conditional flows, mixed merge modes, nested flowcharts, and token handling.
- Updated project structure to include new test classes under the `Flow` folder.

* Remove obsolete tag from `FlowJoin` activity description

* Handle both string and enum values in `GetMergeMode` for backwards compatibility

* Group flowchart integration tests into non-parallelizable test collection

- Introduced `FlowchartTestCollection` to prevent parallel execution of flowchart tests due to shared `Flowchart.UseTokenFlow` flag.
- Updated `FlowchartCounterBasedTests` and `FlowchartTokenBasedTests` to implement `IDisposable` and manage `UseTokenFlow` cleanup.

* Remove unused `Flow` folder reference from test project file

* Add `ParallelTests` for unit and integration testing with various scenarios (#6988)

- Added unit tests for `Parallel` activity to ensure proper scheduling of child activities, including empty and mixed activity cases.
- Added integration tests to validate execution flow and edge cases for `Parallel` activities (e.g., nested parallelism, fault handling).
- Enhanced `ScheduleChildrenAsync` in `Parallel` to handle no activity scenario by completing immediately.

* Fix null-check and memory declaration in `ActivityTestFixture` to prevent potential `NullReferenceException`.

* Introduce Scheduler Strategy Interfaces and Implementations for Workflow and Activity Execution Contexts (#6984)

* Introduce Scheduler Strategy Interfaces and Implementations for Workflow and Activity Execution Contexts

- Added `IWorkflowExecutionContextSchedulerStrategy` and `IActivityExecutionContextSchedulerStrategy` interfaces.
- Implemented `WorkflowExecutionContextSchedulerStrategy` and `ActivityExecutionContextSchedulerStrategy` for scheduling activities in workflows.
- Refactored scheduling logic to utilize the new scheduler strategies.
- Updated unit tests and test helpers to reflect refactoring, introducing fake implementations for testing purposes.
- Adjusted background execution scheduling and improved extensibility for custom scheduler strategies.

* Refactor `SendHttpRequestTests`: simplify scheduling assertions, use shared extensions, and standardize method naming. Streamline helper methods and remove unused test logic.

* Refactor: Replace `ActivityTestHelper` with `ActivityTestFixture` in unit tests for streamlined activity testing

- Introduced `ActivityTestFixture` with a fluent API for better test setup and execution of activities.
- Added extension methods `ActivityTestFixtureExtensions` and `ActivityTestFixtureHttpExtensions` for configuring attributes and HTTP services.
- Updated test guidelines and unit tests to use the new fixture and extensions.
- Removed `ActivityTestHelper`.

* Refactor: Move `ActivityTestFixture` and related extensions to shared project for reuse across test suites

- Consolidated `ActivityTestFixture`, `ActivityTestFixtureExtensions`, and `ActivityTestFixtureHttpExtensions` into `Elsa.Testing.Shared`.
- Updated namespaces and imports across unit tests to reflect new structure.
- Enhanced `AssertActivityAttributes` and added fluent configuration APIs.
- Adjusted `Directory.Packages.props` with new dependencies, including `NSubstitute` and `xunit.assert`.

* Refactor `SetVariableTests`: inline `ActivityTestFixture` initialization to simplify test setup.

* Refactor `ActivityTestFixture`: eliminate redundant field `_services`, add `UsedImplicitly` attributes, and improve service collection management

* Apply suggestion from @Copilot

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

* Add XML documentation for `ActivityExecutionContextExtensions`, detailing methods and parameters.

* Apply suggestion from @Copilot

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

* Add XML documentation for scheduler strategies and their methods

- Updated `IActivityExecutionContextSchedulerStrategy` and `IWorkflowExecutionContextSchedulerStrategy` interfaces with XML summaries for methods.
- Added XML documentation to implementations (`ActivityExecutionContextSchedulerStrategy`, `WorkflowExecutionContextSchedulerStrategy`) and test fakes for clarity.

* Refactor `WriteLineTests`: consolidate duplicate test logic, simplify setup with shared helper method, and enhance readability in assertions.

* Refactor `WriteLineTests` and `SetVariableTests`: replace `WriteLineAsync` assertions with synchronous `WriteLine`, streamline exception recording in `SetVariableTests`, and remove unused imports.

* Add `ParallelTests` for unit and integration testing with various scenarios (#6988)

- Added unit tests for `Parallel` activity to ensure proper scheduling of child activities, including empty and mixed activity cases.
- Added integration tests to validate execution flow and edge cases for `Parallel` activities (e.g., nested parallelism, fault handling).
- Enhanced `ScheduleChildrenAsync` in `Parallel` to handle no activity scenario by completing immediately.

---------

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

* Add new unit and integration tests for activity input and expression evaluation (#6990)

* Introduce Scheduler Strategy Interfaces and Implementations for Workflow and Activity Execution Contexts

- Added `IWorkflowExecutionContextSchedulerStrategy` and `IActivityExecutionContextSchedulerStrategy` interfaces.
- Implemented `WorkflowExecutionContextSchedulerStrategy` and `ActivityExecutionContextSchedulerStrategy` for scheduling activities in workflows.
- Refactored scheduling logic to utilize the new scheduler strategies.
- Updated unit tests and test helpers to reflect refactoring, introducing fake implementations for testing purposes.
- Adjusted background execution scheduling and improved extensibility for custom scheduler strategies.

* Refactor `SendHttpRequestTests`: simplify scheduling assertions, use shared extensions, and standardize method naming. Streamline helper methods and remove unused test logic.

* Refactor: Replace `ActivityTestHelper` with `ActivityTestFixture` in unit tests for streamlined activity testing

- Introduced `ActivityTestFixture` with a fluent API for better test setup and execution of activities.
- Added extension methods `ActivityTestFixtureExtensions` and `ActivityTestFixtureHttpExtensions` for configuring attributes and HTTP services.
- Updated test guidelines and unit tests to use the new fixture and extensions.
- Removed `ActivityTestHelper`.

* Refactor: Move `ActivityTestFixture` and related extensions to shared project for reuse across test suites

- Consolidated `ActivityTestFixture`, `ActivityTestFixtureExtensions`, and `ActivityTestFixtureHttpExtensions` into `Elsa.Testing.Shared`.
- Updated namespaces and imports across unit tests to reflect new structure.
- Enhanced `AssertActivityAttributes` and added fluent configuration APIs.
- Adjusted `Directory.Packages.props` with new dependencies, including `NSubstitute` and `xunit.assert`.

* Refactor `SetVariableTests`: inline `ActivityTestFixture` initialization to simplify test setup.

* Refactor `ActivityTestFixture`: eliminate redundant field `_services`, add `UsedImplicitly` attributes, and improve service collection management

* Apply suggestion from @Copilot

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

* Add XML documentation for `ActivityExecutionContextExtensions`, detailing methods and parameters.

* Apply suggestion from @Copilot

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

* Add XML documentation for scheduler strategies and their methods

- Updated `IActivityExecutionContextSchedulerStrategy` and `IWorkflowExecutionContextSchedulerStrategy` interfaces with XML summaries for methods.
- Added XML documentation to implementations (`ActivityExecutionContextSchedulerStrategy`, `WorkflowExecutionContextSchedulerStrategy`) and test fakes for clarity.

* Refactor `WriteLineTests`: consolidate duplicate test logic, simplify setup with shared helper method, and enhance readability in assertions.

* Refactor `WriteLineTests` and `SetVariableTests`: replace `WriteLineAsync` assertions with synchronous `WriteLine`, streamline exception recording in `SetVariableTests`, and remove unused imports.

* Add new unit and integration tests for activity input and expression evaluation

- Introduced comprehensive test suites covering activity input evaluation, expression handling, and fault scenarios.
- Added unit tests for `ExpressionDescriptorRegistry`, `ExpressionEvaluator`, and activity execution context extensions.
- Added integration tests: `CustomInputEvaluatorTests`, `InputEvaluationErrorTests`, and `InputPropertyEvaluationTests`.
- Enhanced code coverage for edge cases and async evaluation logic.

* Remove unused `RunWorkflowAsync` extension method from `RunActivityExtensions`.

* Rename `CustomInputEvaluatorTests` to `InputEvaluationTests` for consistency with naming conventions.

* Remove unused `Elsa.Workflows.Activities` import from `RunActivityExtensions`.

* Remove redundant comments from `InputEvaluationErrorTests` for clarity.

* Refactor unit tests to streamline activity and expression evaluations

- Refactored `ExecuteActivityAsync` and `ExecuteWriteLineAsync` into shared helpers for consistency and reuse across tests.
- Replaced redundant mock setups with helper methods in `ExpressionDescriptorRegistryTests`.
- Simplified test setup for expression and activity evaluation by removing unused imports and consolidating configuration logic.
- Enhanced readability by reducing duplicate code and leveraging shared utility methods.

* Refactor activity input evaluation tests

- Extracted `CreateContextAsync` helper into `EvaluationTestHelpers` for reuse across evaluation test suites.
- Replaced inline activity context setup with shared helper in `InputPropertyEvaluationTests`, `WrappedInputEvaluationTests`, and related test suites.
- Simplified test method names for clarity and consistency.
- Updated test annotations to enhance readability and align with naming conventions.

* Remove redundant test cases and unused imports

- Deleted duplicated and non-essential test cases across evaluation test suites.
- Removed unused imports to improve code cleanliness and readability.
- Streamlined variable initializations and method calls within test setups.

* Remove redundant test case from `InputEvaluationErrorTests`

- Deleted the `ContinuesEvaluationForMultipleInputs` test, as it overlaps with existing tests and does not provide additional coverage.

* Remove redundant assertion from `InputPropertyEvaluationTests`

- Deleted `Assert.True(context.GetHasEvaluatedProperties())`, as it is unnecessary for verifying test outcomes.

* Remove redundant test cases from `WrappedInputEvaluationTests`

- Deleted `UsesDefaultValueWhenInputIsNull` and `EvaluatesExpression` tests as they are either duplicated or unnecessary for current test coverage.

* Add unit test projects for `Elsa.Workflows.Management` and `Elsa.Expressions`

- Introduced new test projects to separate and organize unit tests for `Elsa.Workflows.Management` and `Elsa.Expressions`.
- Updated `Elsa.sln` to include references to the newly added test projects.
- Adjusted namespaces in affected test classes for consistency with the updated project structure.

* Refactor `ExpressionEvaluatorTests` for clarity and consistency

- Simplified test method names and annotations for improved readability.
- Replaced duplicate mock setups with helper functions (`CreateContextAsync`, `CreateContextWithMockHandlerAsync`, and related methods).
- Streamlined test setups by removing redundant code and consolidating context creation logic.
- Updated test annotations to include descriptive `DisplayName` attributes.

* Apply suggestion from @Copilot

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

* Remove redundant comment from `ExpressionEvaluatorTests` for clarity

* Remove redundant blank lines from unit test classes

- Eliminated unnecessary blank lines across `ActivityExecutionContextExtensions` test suites to improve code readability and consistency.

* Update src/modules/Elsa.Expressions/Services/ExpressionEvaluator.cs

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

* Expand `test-guidelines.md` with testing best practices, helper references, and example snippets

- Added detailed guidance on test project organization, updated helper documentation, and streamlined example code for activity unit testing.
- Introduced scheduler strategy information and integration test patterns for deterministic tests.
- Clarified usage of shared infrastructure like `ActivityTestFixture` and `AsyncWorkflowRunner`.

---------

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

* Fix merge issue

* Add missing `using Elsa.Expressions.Models` directive to `SetVariableTests`

* Update `SetVariableTests` and `ActivityTestFixture` to fix exception type assertion and improve test utility execution handling.

* Update `SetVariableTests` and `ActivityTestFixture` to handle null variables, fix exception type assertion, and simplify context usage.

---------

Co-authored-by: lucas.hipolito <lukhipolito@yahoo.com.br>
Co-authored-by: lukhipolito-nexxbiz <lucas.hipolito@nexxbiz.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-22 20:54:58 +02:00

288 lines
11 KiB
C#

using System.Net;
using Elsa.Activities.UnitTests.Http.Helpers;
using Elsa.Extensions;
using Elsa.Http;
using Elsa.Testing.Shared;
using Elsa.Workflows;
using NSubstitute;
namespace Elsa.Activities.UnitTests.Http;
public class SendHttpRequestTests
{
[Theory]
[InlineData("GET", "https://api.example.com/data", "{\"result\": \"success\"}", 200)]
[InlineData("POST", "https://api.example.com/create", "{\"id\": 123}", 201)]
[InlineData("PUT", "https://api.example.com/update", "{\"updated\": true}", 200)]
public async Task Should_Send_Request_And_Handle_Success_Response(string method, string url, string jsonResponse, int expectedStatusCode)
{
// Arrange
var expectedUrl = new Uri(url);
var expectedMethod = new HttpMethod(method);
var expectedHttpStatusCode = (HttpStatusCode)expectedStatusCode;
var requestCapture = new RequestCapture();
var responseHandler = CreateResponseHandler(expectedHttpStatusCode, jsonResponse, requestCapture);
var sendHttpRequest = CreateSendHttpRequest(expectedUrl, method);
// Act
var context = await ExecuteActivityAsync(sendHttpRequest, responseHandler);
// Assert
Assert.NotNull(requestCapture.CapturedRequest);
Assert.Equal(expectedMethod, requestCapture.CapturedRequest.Method);
Assert.Equal(expectedUrl, requestCapture.CapturedRequest.RequestUri);
var statusCodeOutput = context.GetActivityOutput(() => sendHttpRequest.StatusCode);
Assert.Equal(expectedStatusCode, statusCodeOutput);
}
[Theory]
[InlineData("Bearer token123")]
[InlineData("Basic YWRtaW46cGFzcw==")]
[InlineData("ApiKey abc123")]
public async Task Should_Add_Authorization_Header(string authorizationHeader)
{
// Arrange
var expectedUrl = new Uri("https://api.example.com/secure");
var requestCapture = new RequestCapture();
var responseHandler = CreateResponseHandler(HttpStatusCode.OK, null, requestCapture);
var sendHttpRequest = CreateSendHttpRequest(expectedUrl, authorization: authorizationHeader);
// Act
await ExecuteActivityAsync(sendHttpRequest, responseHandler);
// Assert
Assert.NotNull(requestCapture.CapturedRequest);
Assert.NotNull(requestCapture.CapturedRequest.Headers.Authorization);
Assert.Equal(authorizationHeader, requestCapture.CapturedRequest.Headers.Authorization.ToString());
}
[Theory]
[InlineData(new[]{200, 404}, new[]{"mockActivity200", "mockActivity404"}, "mockUnmatchedActivity", HttpStatusCode.NotFound, "mockActivity404")]
[InlineData(new[]{200, 404}, new[]{"mockActivity200", "mockActivity404"}, "mockUnmatchedActivity", HttpStatusCode.InternalServerError, "mockUnmatchedActivity")]
public async Task Should_Schedule_Activity_According_To_Handlers(int[] statusCodes, string[] activityNames, string handler, HttpStatusCode expectedStatusCode, string expectedScheduledActivityName)
{
// Arrange
var (sendHttpRequest, childActivities) = CreateSendHttpRequestWithStatusHandlers(
expectedStatusCodes: [(statusCodes[0], activityNames[0]), (statusCodes[1], activityNames[1])],
unmatchedHandler: handler
);
var responseHandler = CreateResponseHandler(expectedStatusCode);
// Act
var context = await ExecuteActivityAsync(sendHttpRequest, responseHandler);
// Assert that the correct activity was scheduled.
var expectedActivity = childActivities[expectedScheduledActivityName];
var hasScheduledActivity = context.HasScheduledActivity(expectedActivity);
Assert.True(hasScheduledActivity);
}
[Fact]
public async Task Should_Schedule_FailedToConnect_Activity_On_HttpRequestException()
{
// Arrange
var (sendHttpRequest, childActivities) = CreateSendHttpRequestWithErrorHandlers(
failedToConnect: "mockFailedToConnect"
);
var responseHandler = CreateExceptionHandler<HttpRequestException>("Connection failed");
// Act
var context = await ExecuteActivityAsync(sendHttpRequest, responseHandler);
// Assert
var expectedScheduledActivity = childActivities["mockFailedToConnect"];
var hasScheduledExpectedActivity = context.HasScheduledActivity(expectedScheduledActivity);
Assert.True(hasScheduledExpectedActivity);
}
[Fact]
public async Task Should_Schedule_Timeout_Activity_On_TaskCanceledException()
{
// Arrange
var (sendHttpRequest, childActivities) = CreateSendHttpRequestWithErrorHandlers(
timeout: "mockTimeout"
);
var responseHandler = CreateExceptionHandler<TaskCanceledException>("Request timed out");
// Act
var context = await ExecuteActivityAsync(sendHttpRequest, responseHandler);
// Assert
var expectedScheduledActivity = childActivities["mockTimeout"];
var hasScheduledExpectedActivity = context.HasScheduledActivity(expectedScheduledActivity);
Assert.True(hasScheduledExpectedActivity);
}
[Fact]
public async Task Should_Schedule_No_Activity_When_No_Status_Code_Cases_Match_And_No_Unmatched_Handler()
{
// Arrange
var (configured, _) = CreateSendHttpRequestWithStatusHandlers([(200, "handler200")], unmatchedHandler: null);
var responseHandler = CreateResponseHandler(HttpStatusCode.InternalServerError); // 500 - no match
// Act
var context = await ExecuteActivityAsync(configured, responseHandler);
// Assert
var allScheduledActivities = context.WorkflowExecutionContext.Scheduler.List().ToList();
Assert.Empty(allScheduledActivities);
}
[Fact]
public async Task Should_Set_Response_Headers_Output()
{
// Arrange
var expectedHeaders = new Dictionary<string, string>
{
{ "Custom-Header", "CustomValue" },
{ "X-Rate-Limit", "100" }
};
var responseHandler = CreateResponseHandler(HttpStatusCode.OK, additionalHeaders: expectedHeaders);
var sendHttpRequest = CreateSendHttpRequest(new("https://api.example.com/headers"));
// Act
var context = await ExecuteActivityAsync(sendHttpRequest, responseHandler);
// Assert
var responseHeadersObj = context.GetActivityOutput(() => sendHttpRequest.ResponseHeaders);
var responseHeaders = responseHeadersObj as HttpHeaders;
Assert.NotNull(responseHeaders);
Assert.True(responseHeaders.ContainsKey("Custom-Header"));
Assert.True(responseHeaders.ContainsKey("X-Rate-Limit"));
}
[Fact]
public void Should_Have_Correct_Activity_Attributes()
{
var fixture = new ActivityTestFixture(new SendHttpRequest());
fixture.AssertActivityAttributes(
expectedNamespace: "Elsa",
expectedCategory: "HTTP",
expectedDisplayName: "HTTP Request",
expectedDescription: "Send an HTTP request.",
expectedKind: ActivityKind.Task
);
}
// Private helper methods placed after all public members
private static Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> CreateResponseHandler(
HttpStatusCode statusCode,
string? content = null,
RequestCapture? requestCapture = null,
Dictionary<string, string>? additionalHeaders = null)
{
return (request, _) =>
{
if (requestCapture != null)
requestCapture.CapturedRequest = request;
return Task.FromResult(ActivityTestFixtureHttpExtensions.CreateHttpResponse(statusCode, content, additionalHeaders));
};
}
private sealed class RequestCapture
{
public HttpRequestMessage? CapturedRequest { get; set; }
}
private static SendHttpRequest CreateSendHttpRequest(
Uri url,
string method = "GET",
object? content = null,
string? contentType = null,
string? authorization = null)
{
return new()
{
Url = new(url),
Method = new(method),
Content = content != null ? new Input<object?>(content) : null!,
ContentType = contentType != null ? new Input<string?>(contentType) : null!,
Authorization = authorization != null ? new Input<string?>(authorization) : null!,
ExpectedStatusCodes = new List<HttpStatusCodeCase>()
};
}
private static Task<ActivityExecutionContext> ExecuteActivityAsync(
SendHttpRequest sendHttpRequest,
Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> responseHandler)
{
return new ActivityTestFixture(sendHttpRequest).WithHttpServices(responseHandler).ExecuteAsync();
}
private static (SendHttpRequest sendHttpRequest, Dictionary<string, IActivity> childActivities) CreateSendHttpRequestWithStatusHandlers(
(int statusCode, string activityName)[] expectedStatusCodes,
string? unmatchedHandler)
{
var childActivities = new Dictionary<string, IActivity>();
// Create mock activities for expected status codes
var expectedStatusCodeCases = expectedStatusCodes.Select(x =>
{
var mockActivity = Substitute.For<IActivity>();
childActivities[x.activityName] = mockActivity;
return new HttpStatusCodeCase(x.statusCode, mockActivity);
}).ToList();
// Create mock activity for unmatched handler
var unmatchedActivity = Substitute.For<IActivity>();
if (unmatchedHandler is not null)
{
childActivities[unmatchedHandler] = unmatchedActivity;
}
var sendHttpRequest = new SendHttpRequest
{
Url = new(new Uri("https://api.example.com/test")),
Method = new("GET"),
ExpectedStatusCodes = expectedStatusCodeCases,
UnmatchedStatusCode = unmatchedHandler is not null ? unmatchedActivity : null
};
return (sendHttpRequest, childActivities);
}
private static (SendHttpRequest sendHttpRequest, Dictionary<string, IActivity> childActivities) CreateSendHttpRequestWithErrorHandlers(
string? failedToConnect = null,
string? timeout = null)
{
var childActivities = new Dictionary<string, IActivity>();
IActivity? failedToConnectActivity = null;
IActivity? timeoutActivity = null;
if (failedToConnect != null)
{
failedToConnectActivity = Substitute.For<IActivity>();
childActivities[failedToConnect] = failedToConnectActivity;
}
if (timeout != null)
{
timeoutActivity = Substitute.For<IActivity>();
childActivities[timeout] = timeoutActivity;
}
var sendHttpRequest = new SendHttpRequest
{
Url = new(new Uri("https://api.example.com/error")),
Method = new("GET"),
ExpectedStatusCodes = new List<HttpStatusCodeCase>(),
FailedToConnect = failedToConnectActivity,
Timeout = timeoutActivity
};
return (sendHttpRequest, childActivities);
}
private static Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> CreateExceptionHandler<TException>(string message)
where TException : Exception
{
return (_, _) => throw ((TException)Activator.CreateInstance(typeof(TException), message)!);
}
}