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; } }