using System.Net;
using System.Text;
using Elsa.Http;
using Elsa.Http.ContentWriters;
using Elsa.Http.Parsers;
using Elsa.Resilience;
using Elsa.Testing.Shared;
using Elsa.Workflows;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
namespace Elsa.Activities.UnitTests.Http.Helpers;
///
/// Extension methods for configuring HTTP-related services in ActivityTestFixture.
///
public static class ActivityTestFixtureHttpExtensions
{
///
/// Configures services commonly needed for HTTP activity testing.
/// This is a convenience method that combines HTTP services, mock HTTP client factory, and mock resilient invoker.
///
/// The test fixture to configure
/// Handler for HTTP requests, or null to use a default OK response
/// The fixture instance for method chaining
public static ActivityTestFixture WithHttpServices(
this ActivityTestFixture fixture,
Func>? responseHandler = null)
{
fixture.ConfigureServices(services =>
{
var defaultHandler = responseHandler ?? ((_, _) => Task.FromResult(CreateHttpResponse(HttpStatusCode.OK)));
var mockHttpClientFactory = CreateMockHttpClientFactory(defaultHandler);
services.AddSingleton(mockHttpClientFactory);
services.AddSingleton(CreateMockResilientActivityInvoker());
AddHttpServices(services);
services.AddLogging();
});
return fixture;
}
///
/// Creates a simple HTTP response message with specified status code and optional JSON content.
///
/// The HTTP status code for the response
/// Optional JSON content for the response body
/// Optional additional headers to add to the response
/// An HttpResponseMessage configured with the specified parameters
public static HttpResponseMessage CreateHttpResponse(
HttpStatusCode statusCode,
string? jsonContent = null,
Dictionary? additionalHeaders = null)
{
var response = new HttpResponseMessage(statusCode);
if (jsonContent != null)
{
response.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
}
if (additionalHeaders != null)
{
foreach (var header in additionalHeaders)
{
response.Headers.Add(header.Key, header.Value);
}
}
return response;
}
///
/// Adds all HTTP-related services to the service collection.
/// This includes content factories, parsers, and HTTP client services.
///
private static void AddHttpServices(IServiceCollection services)
{
// Add all required HTTP services
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
// Add HTTP content parsers
AddHttpContentParsers(services);
// Add other required services
services.AddHttpClient();
}
///
/// Adds HTTP content parsers to the service collection.
/// These parsers are responsible for parsing different content types in HTTP responses.
///
private static void AddHttpContentParsers(IServiceCollection services)
{
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
}
///
/// Creates a mock IResilientActivityInvoker that directly executes the provided action.
/// Useful for testing activities that depend on resilient execution without the complexity of retry policies.
///
private static IResilientActivityInvoker CreateMockResilientActivityInvoker()
{
var mock = Substitute.For();
// Configure the mock to simply execute the provided action directly
mock.InvokeAsync(
Arg.Any(),
Arg.Any(),
Arg.Any>>(),
Arg.Any())
.Returns(callInfo =>
{
var action = callInfo.ArgAt>>(2);
return action.Invoke();
});
return mock;
}
///
/// Creates a mock HTTP client factory with a test handler for controlled HTTP responses.
///
private static IHttpClientFactory CreateMockHttpClientFactory(
Func> responseHandler)
{
var mockHttpClientFactory = Substitute.For();
var testHandler = new TestHttpMessageHandler(responseHandler);
var httpClient = new HttpClient(testHandler);
mockHttpClientFactory.CreateClient(Arg.Any()).Returns(httpClient);
return mockHttpClientFactory;
}
}